STATUS: 故障中
C++のクラスのメンバ関数をコールバック関数とする場合、そのメンバ関数をstatic関数にして、コールバック時にthisポインタを渡してもらいます。
【参考URL】
コールバック関数をクラスのメンバとするには?
MFC でコールバック関数を使う
さて、lua言語を組み込んで独自機能を追加する場合、関数ポインタを設定してコールバック的に呼び出してもらうわけですが、この場合もthisポインタを返してもらう必要があります。
でもluaスクリプト内で、thisポインタを扱わせるのは危なっかしいです。値を間違ったり省略したら即落ちますし、スクリプト中でいちいちthisポインタ渡すのがめんどくさい。
コールバック関数内で、thisポインタの有効性をチェックしたり、ポインタの変わりにIDか何かを使ってもいいのですが、パフォーマンスが心配です。
● 改善案
thisポインタをセットしてからコールバック関数を呼び出す(マシン語)コードを生成して、そのコードへのポインタをコールバック関数として設定すれば、よいのではないかと考えました。
以下の、CallbackHelperクラスでは、上記の処理を実装したものです(ただし、C++もどきなテケトーコードです)。
使用例では、lua_register関数の第3引数として、メンバ関数MyFuncをコールバック関数として登録しています。
コールバック関数が呼ばれる際は、thisポインタもちゃんと渡されるため、static関数にする必要はありません。便利~。
登録するコールバック関数の数だけCallbackHelperのインスタンスが必要になりますが、1個10byteそこらなので問題ないでしょう。
【コールバックヘルパクラス】(概念的なソース)
class CallbackHelper {
BYTE byteCode[10];
void* Create( void *pThis , void* pfCallback ) {
// コールバック関数にthisポインタを渡すコードを生成する
// thisポインタを設定(ecxレジスタと仮定 コンパイラ依存)
byteCode[0] = 0xb9; // mov ecx,pThis
byteCode[1 .. 4] = pThis;
// 本来のコールバック関数へジャンプ
byteCode[5] = 0x0f; // jp pfCallback
byteCode[6 .. 9] = pfCallback;
return byteCode;
}
};
【使用例】
class MyClass {
CallbackHelper ch;
lua_Statech *m_pLua;
MyClass() {
m_pLua = lua_open();
lua_baselibopen(m_pLua);
lua_register( m_pLua, "MyFunc",ch.Create(this,MyFunc) ); // ココでコールバック関数を登録
// 以下略
}
int MyFunc( lua_State *L ); // コールバック関数
};
● 改善案の問題点
・データ領域でコードを実行するので、WinXPの新しいSPでは動かないかも
・ヘルパクラスで生成するコードがコンパイラ依存
・そもそもこれ動くのか?
・luaの場合は、適当なグローバル変数にでもthisポインタを突っ込んでおいてコールバック関数(static)内で勝手取得して使えば良いということに気づいてしまった…
・lua_State自体にthisポインタを指定できたら便利だな(実は出来たりして)
アレゲはアレゲ以上のなにものでもなさげ -- アレゲ研究家