RubyでC言語拡張のやりかた入門してみる
link
#
【超入門】キミにも作れる! Ruby拡張ライブラリ開発 - Qiita
- RubyにはCのAPI
rb_define_class
が用意されている - ちなみに
rb_funcall
は遅いので使わないほうがよいらしい- まぁ一応できるかなぁ
- もっとチュートリアルやっておきたいな
- Rubyの拡張ライブラリを作ってみよう! - ser1zw's blog
- ふーむ
- RubyにC言語でFizzbuzzのあたらしいgem足すことできるかな?
- まずはRubyでFizzBuzzメソッドつくってみよう
class FizzBuzz def fizzbuzz(arg) arg.each do |t| if t % 5 ==0 && t % 3 == 0 puts "fizzbuzz" elsif t % 5 == 0 puts "fizz" elsif t % 3 == 0 puts "buzz" else puts t end end end end sample = FizzBuzz.new() sample.fizzbuzz(1..20)
- ふむ・・・・
- むずかしい
- Ruby の C Extension を作ったのでどういうことをやったかまとめた - 猫型の蓄音機は 1 分間に 45 回にゃあと鳴く
- nyan, って名前のextensionを作る場合は
Init_nyan
って関数が必須 - Init_nyanがエントリーポイントになる
- 初期化処理をそこに書くことになる
- nyan, って名前のextensionを作る場合は
void Init_nyan() { /* nyanの初期化処理 */ }
- 初期化処理はたいてい
- クラスを定義する
- クラスにメソッドを定義する
- とかそういった感じになる
- 下記サンプルは動いた
#include "ruby.h" VALUE output_printnumber(VALUE self, int argc){ int i; for(i=0; i < argc; ++i) { printf("number is %d \n", i); } return 0; } // entry point void Init_printnumber(void) { VALUE cPrintnumber = rb_define_class("Printnumber", rb_cObject); rb_define_method(cPrintnumber, "outputnumber", output_printnumber, 1); }
- なるほど・・・
- なんとなくわかってきたのでいろいろ調べる
- mkmf
- library mkmf (Ruby 2.6.0)
- Makefileを作成するライブラリ
- 通常は extconf.rbという名前のrubyスクリプトからrequireされる
- 書いていく順番
- 第4章 クラスとモジュール
- まずは、エントリーポイントとなる void Init_functionName() を書いていく, 例えば
- function名は hogehoge
- class名は Hogehoge
// entry point void Init_hogehoge(){ /* VALUE型, Rubyのデータとして使用するために使う cHogehoge: 名前はなんでもいいが、たぶん通例として小文字のcをつけたあとに、大文字function名で定義するのがよくあるかたち? rb_define_class: C言語拡張してRubyにClass定義するための関数, たぶんruby.h で定義されてる "Hogehoge": 定義したいクラス名、クラス名なので大文字からはじまる rb_cObject: オブジェクトクラスの派生オブジェクトとして作る、という宣言? */ VALUE cHogehoge = rb_define_class("Hogehoge", rb_cObject); /* rb_define_method: methodを定義しますよ、って宣言で中の引数に必要事項を記述していく 第一引数 cHogehoge: これは上の VALUE cHogehoge で定義した cHogehoge のこと, この定義したHogehogeクラスのデータに対してmethodを定義しますよ、って意味? 第二引数 "hogehoge": string型で定義する、methodの名前, なのでhogehogeとかでなくfugafugaとか好きなやつでなんでもよい、このstringがRubyにいったあと使用されるmethod名になる 第三引数 output_hogehoge: まだ定義してないが、Cコードの中に定義する関数(データ型の処理?)の名前, つまりoutput_hogehogeというのを後ほど定義する, なので名前はその定義と合わせておけばなんでもよい 第四引数 1: methodが取る引数の数、intで定義する? */ rb_define_method(cHogehoge, "hogehoge", output_hogehoge, 1); }
- エントリーポイントを作ったら、記述したメソッド"hogehoge" の動作を output_hogehoge() の定義内に記述していく
/* VALUE: VALUE型, CとRubyでデータの受け渡しをするのに必要となる output_printnumber: データ、これが拡張したい関数の中身となる VALUE self: 先頭に与えられる引数, おまじないみたいなものか・・・? int argc: int型の引数, ここはVALUE argcとかでもいいのかもしれないが、その場合はコードがちょっとかわる --> C言語の型で定義するか、Rubyの型として変換してやるかでなんかかわっていくっぽい */ VALUE output_hogehoge(VALUE self, int argc){ /* あとは中身をC言語で記述してやればよい 例としてfor文で1から10までをprintfする処理を記述 */ int i; for(i=0; i < argc; ++i) { printf("number is %d \n", i); } // C言語なので一応最後にreturn 0 が必要? 無いとエラーになる(はず) return 0; }
#encoding:utf-8 require './printnumber' Printnumber.new.outputnumber(10) #=> 0から20までが出力される, なぜか10で止まらない?
- VALUE型のデータを定義する際、VALUEで引数を定義すると中身はRuby型として変換してやらないといけないっぽい
- Ruby - C言語で拡張 - ゆるやかにくちてゆくこの世界で あがく僕の唯一のかつろ
ので、下記のようにCファイルを修正してやるとちゃんとした動作になる
#include "ruby.h" // 引数もそのままでも動作はするが、一応 int argcではなく VALUE argcにしてやる VALUE output_printnumber(VALUE self, VALUE argc){ int i; // NUM2INT(argc) としてやることで、VALUEをCのデータに変換してやる for(i=0; i < NUM2INT(argc); i++) { printf("number is %d \n", i); } return 0; } // entry point void Init_printnumber(void) { VALUE cPrintnumber = rb_define_class("Printnumber", rb_cObject); rb_define_method(cPrintnumber, "outputnumber", output_printnumber, 1); }