2011-01-02
Objective-CコードからCの構造体を簡単ログ出力(with MacRuby)
MacRuby |
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
後書きスタイルでデコレータ
こういう書き方って有りなんだろうか。
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{
影を描く処理
}
イメージ図:
影なんて見た目いかにもおまけなので、有りかも。
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)
2010-01-29 パーサジェネレータはシンタックスハイライターも生成すべき
DSLが流行ってくると、それ用のシンタックスハイライターも次々と作らなきゃならなくなる。
例えば、RSpecでdescribeとかitがifとかclassとかと同等の色つきしてもらえるとありがたい。
お気に入りのエディタでscalaのキーワードが色つきになってくれないのはストレス。
いずれにせよ、新しい言語が広まるには、開発環境が充実していることも必要なはず。
Parser GeneratorのANTLRはParserを生成するが、そのとき、ついでにに色んなエディタ用のシンタックスハイライターも生成してくれると良い気がするけど、どうだろう。自分が欲しいのは、以下のシンタックスハイライターを全部生成してくれるようなもの。
あと、個人的には使っていないけど
あたりも必要だろうか。コンパイラが文法を理解しているので、シンタックスハイライターもParser Generatorが生成してくれるのが、もっとも適切かと思う。ANTLR自体良く理解していないから、もうそういう機能があるのかもしれない。
OSLOのIntellipadはまさにこれをやっているが、エディタが限定されるのが難点。
