デバッグ情報を使ってリフレクションを実現しよう! というページです。
元ネタは http://d.hatena.ne.jp/shinichiro_h/searchdiary?word=reflection.d
デバッグ情報とはなんでしょうか? 他のページにも書いたことですが、つまるところそれは
プログラムの内部インスペクションに必要な情報
です。
例えば
これらに必要な情報はすべてデバッグ情報に含まれていますし、逆にこういった情報がなければ十分なデバッグ環境を構築することはできません。
「プログラムの内部インスペクション」といえば、連想されるのが「リフレクション」である人も多いと思います。Lispだとそうでもない(?)ような気はしなくもないのですが、ここではそういうことにしておいてください。
デバッグ情報は「プログラムの内部インスペクション」に必要な情報ですが、もしデバッグ情報がなくともリフレクションがあれば内部インスペクションができます。まとめるとつまり、
リフレクションがデバッグ情報の代替機能を担っている
ということになります。ならばこれを逆手にとって、
デバッグ情報によってリフレクションを実装することはできないのか?
と考えるのは当然の成り行きでしょう。実はこれができるのです。
CやC++、D言語などのリフレクションが無い言語にその機能を付加することはできます。デバッグ情報があればそれは常に可能です。
サンプルではint型と引数を一つ持った関数型にしか対応していませんが、拡張するのはそんなに難しくありません。クラスメンバや継承クラスなどへの応用も、多少めんどうですが可能です。
/// リフレクション対象のテスト変数。 static int s_int = 0; // リフレクション対象のテスト関数。 static void sample_call(int x) { printf("Called by reflection (argument = %d).\n", x); } void TestRefrection(DbgEngine &engine) { shared_ptr<DbgSymbol> symbol; // 名前から s_int への参照を取得します。 symbol = Rfl_GetSymbolFromName(engine, "s_int"); if (symbol == NULL) { fprintf(stderr, "Could't find the 's_int'.\n"); return; } // これでアドレスから変数情報を得ることは可能ですが、詐欺っぽいので控えています。 // symbol = Rlf_GetSymbolFromAddr(engine, &s_int); int *int_ptr; if (Rfl_GetInt(symbol, &int_ptr) != 0) { fprintf(stderr, "Failed Rfl_GetInt\n"); return; } printf("s_int = %d\n", s_int); // これで"s_int"の値が変わります。 *int_ptr = 100; printf("*int_ptr = %d\n", *int_ptr); printf("s_int = %d\n", s_int); // 名前から関数への参照を取得します。 symbol = Rfl_GetSymbolFromName(engine, "sample_call"); if (symbol == NULL) { fprintf(stderr, "Could't find the 'sample_call'.\n"); return; } void (* sample_function)(int); if (Rfl_GetFunction(symbol, &sample_function) != 0) { fprintf(stderr, "Failed Rfl_GetFunction\n"); return; } // これで"sample_call"関数が呼ばれます。 sample_function(10); }
s_int = 0 *int_ptr = 100 s_int = 100 Called by reflection (argument = 10).