Objective-CコードからCの構造体を簡単ログ出力(with 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