mrubybind - github
使い方は、コンストラクタ用のヘルパー関数を用意してbind_class()でクラスをバインド、bind_class_method()でメソッドをバインドできる。
Foo* new_foo(int x) { return new Foo(x); } void install_foo_class(mrb_state* mrb) { mrubybind::MrubyBind b(mrb); b.bind_class("Foo", new_foo); b.bind_class_method("Foo", "bar", &Foo::bar); }
するとmrubyから呼び出せるようになる:
foo = Foo.new(123) p foo.bar(567)ガベージコレクト時にはC++のクラスのデストラクタがちゃんと呼ばれる。
以下は実装の説明。
昨日mrubyにC++のクラスを持ち込む方法がわかったので、それを関数バインダを作ったときのようにテンプレートを使ってある程度自動化する。
mrubyのDATA型は型情報mrb_data_typeとして名前と解放関数を必要とする。それをClassBinderというテンプレートクラスを定義して、deleteを呼び出す関数dtorを型ごとに用意してやる:
template <class C> struct ClassBinder { static struct mrb_data_type type_info; static void dtor(mrb_state*, void* p) { C* instance = static_cast<C*>(p); delete instance; } }; template<class C> mrb_data_type ClassBinder<C>::type_info = { "???", dtor };テンプレートクラスにも静的メンバ変数を定義できて、それを解放関数で初期化する(テンプレートで文字列を与える方法がわからなかったので、ダミーとして一律に同じ文字列"???"を与えているが、これでいいんだろうか?)。
任意の型のメンバ関数をテンプレートで受け付けるには、普通の関数の登録時とだいたい同じ具合で
template<class C, class P0> struct ClassBinder<void (C::*)(P0)> { ...などとすればマッチングされる。
相違点として、関数ポインタをmrubyに渡す際にはmrubyのVOIDP型として保持しているが、C++のメンバ関数のポインタは関数ポインタと同じ幅とは限らない(ex. 8バイトに対して16バイト)のでその手段は使えない。なので今回はメンバ関数ポインタをそのサイズのバイト列(文字列)を確保して保持することにした:
template <class Method> void bind_class_method(const char* class_name, const char* method_name, Method m) { ... mrb_value mp = mrb_str_new(mrb, (char*)&m, sizeof(m)); ...使うときにはRSTRING_PTR()で文字列のポインタを取り出して、その中身をメンバ関数ポインタとして扱う。
0 件のコメント :
コメントを投稿