Dec 30, 2007

拡張ライブラリ


mkrf も ruby-opengl 0.60.0 もアップデート来ませんね。しかたがないので、Ruby の拡張ライブラリの作り 方を勉強しながら待つことに(笑
SWIGを試してみたことはありますが、1から 書いたことがなかったので。
お題は数学関数に。うまいこと完成したら、ruby-opengl と一緒に使えるかも しれません。
以下のメモに出てくるRVec4は「4次元ベクトルを表すクラス」として 読んでみてください。

クラスの追加

rb_cRVec4 = rb_define_class( "RVec4", rb_cObject );
rb_cObject は include/ruby/ruby.h で宣言されている組み込みの変数。

メモリ確保関数の指定

rb_define_alloc_func( rb_cRVec4, RVec4_allocate );
RVec4.newと書くと、allocateinitializeの順にメソッド が走るようです。このallocateのときに呼ばれる関数は上記のように指 定できるようになっています。

確保した領域の Ruby オブジェクト(T_DATA)化

RVec4* v = malloc( sizeof(RVec4) );
...
VALUE obj = Data_Wrap_Struct( rb_cRVec4, 0, RVec4_free, v );
C++ でいうPlacement new みたいなものかな? obj の型は TYPE(obj) == T_DATA だった。 この領域を解放する関数は Data_Wrap_Struct の第3引数で指定する。

インスタンスメソッドの追加

rb_define_method( rb_cRVec4, "getLength", RVec4_getLength, -1 );
これで RVec4 クラスのインスタンスに getLength メソッドが追加される。実装は
VALUE RVec4_getLength( int argc, VALUE* argv, VALUE self );
という関数で行う。

クラスメソッド*1の追加

rb_define_singleton_method( rb_cRVec4, "dot", RVec4_dot, -1 );
これで result = RVec4.dot(v1, v2) という感じで利用できることになる。

Ruby の VALUE から C の構造体へのキャスト

VALUE RVec4_getLength( int argc, VALUE* argv, VALUE self )
{
    RVec4* v = NULL;
    ...
    Data_Get_Struct( self, RVec4, v );
    ...

float/doubleのT_FLOAT化

VALUE obj = rb_float_new( flt );

Ruby のメソッドを使う

obj の to_s メソッドを使いたいときは:
VALUE str = rb_funcall( obj, rb_intern("to_s"), 0 );
…となる。 rb_funcall の第3/第4引数にはそれぞれ argc, argv を渡すことができる。
特に rb_funcall がおもしろいですね。Ruby の機能がそのまま C から利用できるというわけですから。
*1  正確には「クラス(を表すオジェクト)に対する特異メソッド」と呼ぶらしい。