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 件のコメント :
コメントを投稿