Hatena::ブログ(Diary)

kyabの日記

2011-01-02

Objective-CコードからCの構造体を簡単ログ出力(with MacRuby)

| 01:47

MacRubyでプロジェクトを作成すれば、Objective-Cのコード中から簡単にCの構造体をダンプできる。

Ruby側。NSValueに詰め込まれた構造体と型名を受け取って、ダンプするユーティリティ。

class Util

   def self.dump_struct_withName(o,klass_name)

      if (o.kind_of?(NSValue))
         pointer = o.pointerValue
         puts pointer.class       #=>Pointer
         
         pointer.cast!(TopLevel.const_get(klass_name).type)
         struct = pointer[0]
         return unless struct.class.respond_to?(:fields)
         puts "dumping struct #{struct.class}"
         struct.class.fields.each do |field_name|
            puts "\t#{field_name.to_s} = #{struct.__send__(field_name)}"
         end
      end
   end
end

Pointerクラスの使い方についてはMacRubyのPointerクラスについて - Watsonのメモを参考に。

MacRuby側ではCの構造体はBoxedというクラスを継承したクラスになり、#fieldsでメンバ一覧が取得できる。


Objective-C(++)側

#include <typeinfo>   
#include "CoreAudio/CoreAudio.h"
#import "Controller.h"
#import "MacRuby/MacRuby.h"   //for [MacRuby sharedRuntime]

#include <string>

//demangle function (gcc only)
//http://d.hatena.ne.jp/hidemon/20080731/1217488497
#include <string>
extern "C" char *__cxa_demangle (
                         const char *mangled_name,
                         char *output_buffer,
                         size_t *length,
                         int *status);

std::string demangle(const char * name) {
    size_t len = strlen(name) + 256;
    char output_buffer[len];
    int status = 0;
    return std::string(
                  __cxa_demangle(name, output_buffer, 
                             &len, &status));
}


//MacRubyへのブリッジ関数
template <typename T>
void dump_struct(const T &t){
   
   //型名を文字列で取得
   const std::type_info &type = typeid(t);
   std::string demangled_type_name = demangle(type.name());
   
   NSValue *v = [NSValue valueWithPointer:&t];
   NSString *typeName = [NSString stringWithCString:demangled_type_name.c_str() encoding:kCFStringEncodingUTF8 ];
   id ruby_util = [[MacRuby sharedRuntime] evaluateString:@"Util"];
   [ruby_util performRubySelector:@selector(dump_struct_withName:) withArguments:v,typeName,NULL];
}

@implementation Controller

- (IBAction)callRubyMethod:(id)sender{
   
   //例えばこういうちょっとメンバが多い構造体でも簡単に列挙できる。
   AudioStreamBasicDescription format;
   format.mSampleRate = 44100.0;
   format.mFormatID = kAudioFormatLinearPCM; //1819304813
   format.mFormatFlags = 41;
   format.mBytesPerPacket = 4;
   format.mFramesPerPacket = 1;
   format.mBytesPerFrame = 4;
   format.mChannelsPerFrame = 2;
   format.mBitsPerChannel = 32;
   format.mReserved = 0;
   
   dump_struct(format);
   
}
@end

結果

dumping struct AudioStreamBasicDescription
	mSampleRate = 44100.0
	mFormatID = 1819304813
	mFormatFlags = 41
	mBytesPerPacket = 4
	mFramesPerPacket = 1
	mBytesPerFrame = 4
	mChannelsPerFrame = 2
	mBitsPerChannel = 32
	mReserved = 0

dumpstruct()では、Ruby側に構造体の型名を渡すためにRTTIとデマングルを使っている。デマングルのためのコードはC++ のtype_info.name() - hidemonの日記からそのまま使いました。

まぁ最初から全部MacRubyで書けよって話もあるだろうけど、メインがObjective-Cのコードで、少しだけ楽をする方法。

プロジェクト一式は

git://github.com/kyab/CallRubyMethod_fromObjC.git

2010-08-16

後書きスタイルでデコレータ

04:15

こういう書き方って有りなんだろうか。

class Proc 
  def sorround_by(&block)
    block.call
    self.call
    block.call
  end
end

lambda{
  puts "one"
  puts "two"
  puts "three"
}.sorround_by{
  puts "-------"
}

結果:

-------
one
two
three
-------

普通は

def sorround_by(srt,&block)
  puts str
  block.call
  puts str
end

sorround_by("-----") do 
  puts "one"
  puts "two"
  puts "three"
end

結果:

-------
one
two
three
-------

という感じでブロックを取る関数を書く。だけど、この例のデコレーションした出力、や何らかの処理を前後に挟む場合、何で囲っているか(sorround_by)はさして重要じゃなくて、その中身のほうがメインだったりする。

つまり、確かにsorround_byしてるんだけど、それは「ちなみに」という程度という状況。

ならば、最初にメインの部分を書いて、その後に「ちなみに」な部分を書いた方が見やすいのかと思った。

lambda{
  メインの処理
}.ちなみに{
  これこれしといて
}

これならメインな処理を頭に持ってきつつ、補足的な「ちなみに」処理を後書きできる。

例:スレッド排他

lambda{
  メインの処理
}.synchronized(@mutex)

微妙。同期って重要だし、先に書けよって感じも。

例:描画処理で影をつける

lambda{
   円を描く処理
   線を描く処理
}.with_shadow{
   影を描く処理
}

イメージ図:

f:id:kyab:20100817040329p:image

影なんて見た目いかにもおまけなので、有りかも。

2010-02-19 [haXe]enumが結構良いかもしれない

ActionScriptのArrayの中身は、Ruby等と同様どんな型も受け付けるのがちょっと嫌で、haXeに乗り換えてやろうかと思い、構文を斜め読みしていたところ、列挙型(enum)が結構見慣れない感じで新鮮だった。

以下公式サイトからの引用を少し修正しただけですが。

   enum Color {
        red;
        green;
        blue;
   }

    class Colors {
        
        //ColorからRGB値への変換
        static function toInt( c : Color ) : Int {
            return switch( c ) {
                case red: 0xFF0000;
                case green: 0x00FF00;
                //case blue:がない。defaultもない。
            }
        }
    }

このコードは、コンパイル時にエラー(blueに対する処理が無い)にしてくれる。

C++でのenumには、switchで判別しなきゃいけない場合にcase漏れを検証しきれないという不安が常に付きまとっていたのですが、こういうアプローチでの解決もあるのかと妙に感心。

更に、enumの各要素(構成子と呼ばれるらしい)は、red,greenなどの定数構成子に加えて、パラメータをとることもできる。

    enum Color2{
        red;
        green;
        blue;
        grey(v:Int);
        rgb(r:Int, g:Int, b:Int);
    }
    
    //次の値はどれもColor2
    red;
    green;
    grey(0);
    grey(128);
    rgb(0xAD,0xD8,0xE6); //Light Blue

これだけだと何の意味があるのかわかりませんが、switchと組み合わせて使うときに効力を発揮する。

    class Colors{
        static function toInt( c : Color2 ) : Int {
            return switch( c ) {
                case red: 0xFF0000;
                case green: 0x00FF00;
                case blue: 0x0000FF;
                case grey(v): (v << 16) | (v << 8) | v;
                case rgb(r,g,b): (r << 16) | (g << 8) | b;
            }
        }
    }

これって、明示的には言及されていないけど、ある種のパターンマッチ?

2010-02-02 WYWIWYM(What You Write Is What You Mean)

WYSIWYG - Wikipedia(What You See Is What You Get)という言葉がある。

プログラミングの世界でも、書かれていることがプログラマが意図する(頭の中にある)ことそのものに近づいて来ている。その流れが非常に速くなり、必要なコストも低くなってきている。

Intention Programming、DSL関数型(宣言型)言語をまとめてWYWIWYMと呼ぶのはどうだろうかとふと思った。

もちろん、機械語の時代からそれはずっと続いてきたことだし、これからも進んで行くのだろう。まぁどうであれプログラミングは面白い。

2010-01-29 パーサジェネレータはシンタックスハイライターも生成すべき

DSLが流行ってくると、それ用のシンタックスハイライターも次々と作らなきゃならなくなる。

例えば、RSpecでdescribeとかitがifとかclassとかと同等の色つきしてもらえるとありがたい。

お気に入りのエディタscalaキーワードが色つきになってくれないのはストレス

いずれにせよ、新しい言語が広まるには、開発環境が充実していることも必要なはず。

Parser GeneratorのANTLRはParserを生成するが、そのとき、ついでにに色んなエディタ用のシンタックスハイライターも生成してくれると良い気がするけど、どうだろう。自分が欲しいのは、以下のシンタックスハイライターを全部生成してくれるようなもの。


あと、個人的には使っていないけど

あたりも必要だろうか。コンパイラが文法を理解しているので、シンタックスハイライターもParser Generatorが生成してくれるのが、もっとも適切かと思う。ANTLR自体良く理解していないから、もうそういう機能があるのかもしれない。

OSLOのIntellipadはまさにこれをやっているが、エディタが限定されるのが難点。