Ruby の C Extension を作ったのでどういうことをやったかまとめた

waveずたずたのための ruby の C Extension 書いたんだけど、どうやって書いたのかまとめると有益っぽいのでまとめた。

ビルドするためにどうすんの

Cだから当然コンパイルしないと動かない。普通は make でビルドしたいと思うんだけど、そのための仕組みとして mkmf ってのがある。ドキュメントにも書いてあるけど、 exconf.rb っていうファイルを作って読み込んで使いたいライブラリとかいろいろあるならそれを extconf.rb に書いた上で create_makefile('name_of_extension_library') みたいなの書く。

$ ruby extconf.rb

すると Makefile 出来上がるので、make すればビルドできる。当然 C コンパイラとかないとダメ。

have_library('hoge') とかやるともしそのライブラリがある場合はちゃんと -llibrary みたいなオプションつけた Makefike 作るようになっててめっちゃ優秀。いいね!って感じだった。

C のコードはどうすんの

nyan って名前の extension 作りたいなら、 Init_nyan っていう関数が必須。ていうか Init_nyan がエントリーポイントになる。初期化処理をここに書く。

void
Init_nyan()
{
    /* nyan の初期化処理 */
}

初期化処理はたいてい

  • クラスを定義する
  • クラスにメソッド定義する

とかそういう感じになると思う。

C の世界で Ruby の世界のクラスを定義するのは rb_define_class とかなんだけど、そういう「C の世界で Ruby の世界のアレをこうしたい」みたいなときには公式に C から Rubyの世界を触るための関数のドキュメント 読むと載ってる。

C の構造体と Ruby のクラスを紐づけたいんだけど

Ruby の世界のオブジェクトと C の世界世界の構造体を紐づけたいなーってことあると思う。C の世界で Ruby のオブジェクトは VALUE 型で表されてるけど、「VALUE 型 に C の世界の struct 埋め込んでおいて、Ruby のメソッド呼び出しされたときにレシーバの VALUE からその構造体取り出したいなー」みたいなやつ。

これどうするのか全然知らなかったんだけど、@takkkun が書いてた形態素解析のベンチのコード読んだらそれが出てきてすごい参考になった。

rb_define_alloc_func っていうので Ruby のオブジェクトが new されるときの処理(initializeされるまえの、メモリ確保するところの処理)を指定できるようになってる。いっこめの引数はcの世界での Ruby のクラス(当然VALUE型)で、にこめの引数に、new されるときに呼ばれる allocate 処理の関数ポインタ渡す。

で、そのとき関数ポインタで渡した関数が実際に allocate する処理なわけだけど、ポイントはここで Data_Wrap_Struct っていう関数呼んでるところで、これで VALUE に C の世界の struct を wrap できる。VALUE から wrap されてる struct を取り出すのは Data_Get_Struct 。このあたりは上述のたっくんのコードがミニマルかつシンプルかつ必要充分なサンプルになってるのでそれを読むといいと思う。

◯◯したいときはどうすんの

上述のところで基本的なことは書けると思うんだけど、「Ruby の世界の例外を C から上げたいんだけど」とかいろいろ「これはどうすんの」みたいなのあると思う。そういうときにはRuby組み込むクラスのソース読むとたいてい「あーこうやればいいんだ」みたいなの出てくる。C Extension 書くときは手元に CRuby のソースコードをリファレンスとして置いておくと良い。概要っぽいものがわかったあとは CRuby の組み込みクラスが一番の教科書だと思う。