.\" README.EXT.ja - -*- Text -*- created at: Mon Aug 7 16:45:54 JST 1995 Rubyの拡張ライブラリの作り方を説明します. 1.基礎知識 Cの変数には型があり,データには型がありません.ですから,た とえばポインタをintの変数に代入すると,その値は整数として取 り扱われます.逆にRubyの変数には型がなく,データに型がありま す.この違いのため,CとRubyは相互に変換しなければ,お互いの データをアクセスできません. RubyのデータはVALUEというCの型で表現されます.VALUE型のデー タはそのデータタイプを自分で知っています.このデータタイプと いうのはデータ(オブジェクト)の実際の構造を意味していて,Ruby のクラスとはまた違ったものです. VALUEからCにとって意味のあるデータを取り出すためには (1) VALUEのデータタイプを知る (2) VALUEをCのデータに変換する の両方が必要です.(1)を忘れると間違ったデータの変換が行われ て,最悪プログラムがcore dumpします. 1.1 データタイプ Rubyにはユーザが使う可能性のある以下のタイプがあります. T_NIL nil T_OBJECT 通常のオブジェクト T_CLASS クラス T_MODULE モジュール T_FLOAT 浮動小数点数 T_STRING 文字列 T_REGEXP 正規表現 T_ARRAY 配列 T_HASH 連想配列 T_STRUCT (Rubyの)構造体 T_BIGNUM 多倍長整数 T_FIXNUM Fixnum(31bitまたは63bit長整数) T_COMPLEX 複素数 T_RATIONAL 有理数 T_FILE 入出力 T_TRUE 真 T_FALSE 偽 T_DATA データ T_SYMBOL シンボル その他に内部で利用されている以下のタイプがあります. T_ICLASS T_MATCH T_UNDEF T_NODE T_ZOMBIE ほとんどのタイプはCの構造体で実装されています. 1.2 VALUEのデータタイプをチェックする ruby.hではTYPE()というマクロが定義されていて,VALUEのデータ タイプを知ることが出来ます.TYPE()マクロは上で紹介したT_XXXX の形式の定数を返します.VALUEのデータタイプに応じて処理する 場合には,TYPE()の値で分岐することになります. switch (TYPE(obj)) { case T_FIXNUM: /* FIXNUMの処理 */ break; case T_STRING: /* 文字列の処理 */ break; case T_ARRAY: /* 配列の処理 */ break; default: /* 例外を発生させる */ rb_raise(rb_eTypeError, "not valid value"); break; } それとデータタイプをチェックして,正しくなければ例外を発生す る関数が用意されています. void Check_Type(VALUE value, int type) この関数はvalueがtypeで無ければ,例外を発生させます.引数と して与えられたVALUEのデータタイプが正しいかどうかチェックす るためには,この関数を使います. FIXNUMとNILに関してはより高速な判別マクロが用意されています. FIXNUM_P(obj) NIL_P(obj) 1.3 VALUEをCのデータに変換する データタイプがT_NIL,T_FALSE,T_TRUEである時,データはそれぞ れnil,false,trueです.このデータタイプのオブジェクトはひと つずつしか存在しません. データタイプがT_FIXNUMの時,これは31bitまたは63bitのサイズを 持つ整数です.longのサイズが32bitのプラットフォームであれば 31bitに,longのサイズが64bitのプラットフォームであれば63bit になります. FIXNUM を C の整数に変換するためにはマクロ 「FIX2INT()」または「FIX2LONG()」を使います.これらのマクロ を使用する際には事前にデータタイプがFIXNUMであることを確認す る必要がありますが、比較的高速に変換を行うことができます.ま た、「FIX2LONG()」は例外を発生しませんが、「FIX2INT()」は変 換結果がintのサイズに収まらない場合には例外を発生します。 それから,FIXNUMに限らずRubyのデータを整数に変換する 「NUM2INT()」および「NUM2LONG()」というマクロがあります.こ れらのマクロはマクロはデータタイプのチェック無しで使えます (整数に変換できない場合には例外が発生する).同様にチェック無 で使える変換マクロはdoubleを取り出す「NUM2DBL()」があります. char* を取り出す場合,version 1.6 以前では「STR2CSTR()」とい うマクロを使っていましたが,これは to_str() による暗黙の型変 換結果が GC される可能性があるため,version 1.7 以降では obsolete となり,代わりに StringValue() と StringValuePtr() を使う事を推奨しています.StringValue(var) は var が String であれば何もせず,そうでなければ var を var.to_str() の結果 に置き換えるマクロ,StringValuePtr(var) は同様に var を String に置き換えてから var のバイト列表現に対する char* を 返すマクロです.var の内容を直接置き換える処理が入るので, var は lvalue である必要があります. また,StringValuePtr() に類似した StringValueCStr() というマ クロもあります.StringValueCStr(var) は var を String に置き 換えてから var の文字列表現に対する char* を返します.返され る文字列の末尾には nul 文字が付加されます.なお,途中に nul 文字が含まれる場合は ArgumentError が発生します. 一方,StringValuePtr() では,末尾に nul 文字がある保証はなく, 途中に nul 文字が含まれている可能性もあります. それ以外のデータタイプは対応するCの構造体があります.対応す る構造体のあるVALUEはそのままキャスト(型変換)すれば構造体の ポインタに変換できます. 構造体は「struct RXxxxx」という名前でruby.hで定義されていま す.例えば文字列は「struct RString」です.実際に使う可能性が あるのは文字列と配列くらいだと思います. ruby.hでは構造体へキャストするマクロも「RXXXXX()」(全部大文 字にしたもの)という名前で提供されています(例: RSTRING()). 構造体からデータを取り出すマクロが提供されています.文字列 strの長さを得るためには「RSTRING_LEN(str)」とし,文字列strを char*として得るためには「RSTRING_PTR(str)」とします.配列の 場合には,それぞれ「RARRAY_LEN(ary)」,「RARRAY_PTR(ary)」と なります. Rubyの構造体を直接アクセスする時に気をつけなければならないこ とは,配列や文字列の構造体の中身は参照するだけで,直接変更し ないことです.直接変更した場合,オブジェクトの内容の整合性が とれなくなって,思わぬバグの原因になります. 1.4 CのデータをVALUEに変換する VALUEの実際の構造は * FIXNUMの場合 1bit左シフトして,LSBを立てる. * その他のポインタの場合 そのままVALUEにキャストする. となっています.よって,LSBをチェックすればVALUEがFIXNUMかど うかわかるわけです(ポインタのLSBが立っていないことを仮定して いる). ですから,FIXNUM以外のRubyのオブジェクトの構造体は単にVALUE にキャストするだけでVALUEに変換出来ます.ただし,任意の構造 体がVALUEにキャスト出来るわけではありません.キャストするの はRubyの知っている構造体(ruby.hで定義されているstruct RXxxx のもの)だけです. FIXNUMに関しては変換マクロを経由する必要があります.Cの整数 からVALUEに変換するマクロは以下のものがあります.必要に応じ て使い分けてください. INT2FIX() もとの整数が31bitまたは63bit以内に収まる自信 がある時 INT2NUM() 任意の整数からVALUEへ INT2NUM()は整数がFIXNUMの範囲に収まらない場合,Bignumに変換 してくれます(が,少し遅い). 1.5 Rubyのデータを操作する 先程も述べた通り,Rubyの構造体をアクセスする時に内容の更新を 行うことは勧められません.で,Rubyのデータを操作する時には Rubyが用意している関数を用いてください. ここではもっとも使われるであろう文字列と配列の生成/操作を行 い関数をあげます(全部ではないです). 文字列に対する関数 rb_str_new(const char *ptr, long len) 新しいRubyの文字列を生成する. rb_str_new2(const char *ptr) rb_str_new_cstr(const char *ptr) Cの文字列からRubyの文字列を生成する.この関数の機能は rb_str_new(ptr, strlen(ptr))と同等である. rb_tainted_str_new(const char *ptr, long len) rb_tainted_str_new_cstr(const char *ptr) 汚染マークが付加された新しいRubyの文字列を生成する.外部 からのデータに基づく文字列には汚染マークが付加されるべき である. rb_tainted_str_new2(const char *ptr) Cの文字列から汚染マークが付加されたRubyの文字列を生成する. rb_sprintf(const char *format, ...) rb_vsprintf(const char *format, va_list ap) Cの文字列formatと続く引数をprintf(3)のフォーマットにしたがって 整形し,Rubyの文字列を生成する. rb_str_cat(VALUE str, const char *ptr, long len) Rubyの文字列strにlenバイトの文字列ptrを追加する. rb_str_cat2(VALUE str, const char* ptr) Rubyの文字列strにCの文字列ptrを追加する.この関数の機能は rb_str_cat(str, ptr, strlen(ptr))と同等である. rb_str_catf(VALUE str, const char* format, ...) rb_str_vcatf(VALUE str, const char* format, va_list ap) Cの文字列formatと続く引数をprintf(3)のフォーマットにしたがって 整形し,Rubyの文字列strに追加する.この関数の機能は,それぞれ rb_str_cat2(str, rb_sprintf(format, ...)) や rb_str_cat2(str, rb_vsprintf(format, ap)) と同等である. rb_enc_str_new(const char *ptr, long len, rb_encoding *enc) 指定されたエンコーディングでRubyの文字列を生成する. rb_usascii_str_new(const char *ptr, long len) rb_usascii_str_new_cstr(const char *ptr) エンコーディングがUS-ASCIIのRubyの文字列を生成する. 配列に対する関数 rb_ary_new() 要素が0の配列を生成する. rb_ary_new2(long len) 要素が0の配列を生成する.len要素分の領域をあらかじめ割り 当てておく. rb_ary_new3(long n, ...) 引数で指定したn要素を含む配列を生成する. rb_ary_new4(long n, VALUE *elts) 配列で与えたn要素の配列を生成する. rb_ary_to_ary(VALUE obj) オブジェクトを配列に変換する. Object#to_aryと同等である. 他にも配列を操作する関数が多数ある. これらは 引数aryに配列を渡さなければならない. さもないと コアを吐く. rb_ary_aref(argc, VALUE *argv, VALUE ary) Array#[]と同等. rb_ary_entry(VALUE ary, long offset) ary[offset] rb_ary_subseq(VALUE ary, long beg, long len) ary[beg, len] rb_ary_push(VALUE ary, VALUE val) rb_ary_pop(VALUE ary) rb_ary_shift(VALUE ary) rb_ary_unshift(VALUE ary, VALUE val) 2.Rubyの機能を使う 原理的にRubyで書けることはCでも書けます.RubyそのものがCで記 述されているんですから,当然といえば当然なんですけど.ここで はRubyの拡張に使うことが多いだろうと予測される機能を中心に紹 介します. 2.1 Rubyに機能を追加する Rubyで提供されている関数を使えばRubyインタプリタに新しい機能 を追加することができます.Rubyでは以下の機能を追加する関数が 提供されています. * クラス,モジュール * メソッド,特異メソッドなど * 定数 では順に紹介します. 2.1.1 クラス/モジュール定義 クラスやモジュールを定義するためには,以下の関数を使います. VALUE rb_define_class(const char *name, VALUE super) VALUE rb_define_module(const char *name) これらの関数は新しく定義されたクラスやモジュールを返します. メソッドや定数の定義にこれらの値が必要なので,ほとんどの場合 は戻り値を変数に格納しておく必要があるでしょう. クラスやモジュールを他のクラスの内部にネストして定義する時に は以下の関数を使います. VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super) VALUE rb_define_module_under(VALUE outer, const char *name) 2.1.2 メソッド/特異メソッド定義 メソッドや特異メソッドを定義するには以下の関数を使います. void rb_define_method(VALUE klass, const char *name, VALUE (*func)(), int argc) void rb_define_singleton_method(VALUE object, const char *name, VALUE (*func)(), int argc) 念のため説明すると「特異メソッド」とは,その特定のオブジェク トに対してだけ有効なメソッドです.RubyではよくSmalltalkにお けるクラスメソッドとして,クラスに対する特異メソッドが使われ ます. これらの関数の argcという引数はCの関数へ渡される引数の数(と 形式)を決めます.argcが0以上の時は関数に引き渡す引数の数を意 味します.16個以上の引数は使えません(が,要りませんよね,そ んなに).実際の関数には先頭の引数としてselfが与えられますの で,指定した数より1多い引数を持つことになります. argcが負の時は引数の数ではなく,形式を指定したことになります. argcが-1の時は引数を配列に入れて渡されます.argcが-2の時は引 数はRubyの配列として渡されます. メソッドを定義する関数はまだいくつかあります. ひとつはメソッド 名としてIDを取ります. IDについては2.2.2を参照. void rb_define_method_id(VALUE klass, ID name, VALUE (*func)(ANYARGS), int argc) private/protectedなメソッドを定義するふたつの関数があります. void rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(), int argc) void rb_define_protected_method(VALUE klass, const char *name, VALUE (*func)(), int argc) privateメソッドとは関数形式でしか呼び出すことの出来ないメソッ ドです. 最後に、 rb_define_module関数はモジュール関数を定義します。 モジュール関数とはモジュールの特異メソッドであり,同時に privateメソッドでもあるものです.例をあげるとMathモジュール のsqrt()などがあげられます.このメソッドは Math.sqrt(4) という形式でも include Math sqrt(4) という形式でも使えます.モジュール関数を定義する関数は以下の 通りです. void rb_define_module_function(VALUE module, const char *name, VALUE (*func)(), int argc) 関数的メソッド(Kernelモジュールのprivate method)を定義するた めの関数は以下の通りです. void rb_define_global_function(const char *name, VALUE (*func)(), int argc) メソッドの別名を定義するための関数は以下の通りです. void rb_define_alias(VALUE module, const char* new, const char* old); 属性の取得・設定メソッドを定義するには void rb_define_attr(VALUE klass, const char *name, int read, int write) クラスメソッドallocateを定義したり削除したりするための関数は 以下の通りです. void rb_define_alloc_func(VALUE klass, VALUE (*func)(VALUE klass)); void rb_undef_alloc_func(VALUE klass); funcはクラスを引数として受け取って,新しく割り当てられたイン スタンスを返さなくてはなりません.このインスタンスは,外部リ ソースなどを含まない,できるだけ「空」のままにしておいたほう がよいでしょう. 2.1.3 定数定義 拡張ライブラリが必要な定数はあらかじめ定義しておいた方が良い でしょう.定数を定義する関数は二つあります. void rb_define_const(VALUE klass, const char *name, VALUE val) void rb_define_global_const(const char *name, VALUE val) 前者は特定のクラス/モジュールに属する定数を定義するもの,後 者はグローバルな定数を定義するものです. 2.2 Rubyの機能をCから呼び出す 既に『1.5 Rubyのデータを操作する』で一部紹介したような関数を 使えば,Rubyの機能を実現している関数を直接呼び出すことが出来 ます. # このような関数の一覧表はいまのところありません.ソースを見 # るしかないですね. それ以外にもRubyの機能を呼び出す方法はいくつかあります. 2.2.1 Rubyのプログラムをevalする CからRubyの機能を呼び出すもっとも簡単な方法として,文字列で 与えられたRubyのプログラムを評価する以下の関数があります. VALUE rb_eval_string(const char *str) この評価は現在の環境で行われます.つまり,現在のローカル変数 などを受け継ぎます. 評価は例外を発生するかもしれないことに注意しましょう. より安全 な関数もあります. VALUE rb_eval_string_protect(const char *str, int *state) この関数はエラーが発生するとnilを返します。そして、成功時には *stateはゼロに、さもなくば非ゼロになります。 2.2.2 IDまたはシンボル Cから文字列を経由せずにRubyのメソッドを呼び出すこともできま す.その前に,Rubyインタプリタ内でメソッドや変数名を指定する 時に使われているIDについて説明しておきましょう. IDとは変数名,メソッド名を表す整数です.Rubyの中では :識別子 または :"任意の文字列" でアクセスできます.Cからこの整数を得るためには関数 rb_intern(const char *name) を使います.Rubyから引数として与えられたシンボル(または文字 列)をIDに変換するには以下の関数を使います. rb_to_id(VALUE symbol) 2.2.3 CからRubyのメソッドを呼び出す Cから文字列を経由せずにRubyのメソッドを呼び出すためには以下 の関数を使います. VALUE rb_funcall(VALUE recv, ID mid, int argc, ...) この関数はオブジェクトrecvのmidで指定されるメソッドを呼び出 します.その他に引数の指定の仕方が違う以下の関数もあります. VALUE rb_funcall2(VALUE recv, ID mid, int argc, VALUE *argv) VALUE rb_apply(VALUE recv, ID mid, VALUE args) applyには引数としてRubyの配列を与えます. 2.2.4 変数/定数を参照/更新する Cから関数を使って参照・更新できるのは,定数,インスタンス変 数です.大域変数は一部のものはCの大域変数としてアクセスでき ます.ローカル変数を参照する方法は公開していません. オブジェクトのインスタンス変数を参照・更新する関数は以下の通 りです. VALUE rb_ivar_get(VALUE obj, ID id) VALUE rb_ivar_set(VALUE obj, ID id, VALUE val) idはrb_intern()で得られるものを使ってください. 定数を参照するには以下の関数を使ってください. VALUE rb_const_get(VALUE obj, ID id) 定数を新しく定義するためには『2.1.3 定数定義』で紹介さ れている関数を使ってください. 3.RubyとCとの情報共有 C言語とRubyの間で情報を共有する方法について解説します. 3.1 Cから参照できるRubyの定数 以下のRubyの定数はCのレベルから参照できます. Qtrue Qfalse 真偽値.QfalseはC言語でも偽とみなされます(つまり0). Qnil C言語から見た「nil」. 3.2 CとRubyで共有される大域変数 CとRubyで大域変数を使って情報を共有できます.共有できる大域 変数にはいくつかの種類があります.そのなかでもっとも良く使わ れると思われるのはrb_define_variable()です. void rb_define_variable(const char *name, VALUE *var) この関数はRubyとCとで共有する大域変数を定義します.変数名が `$'で始まらない時には自動的に追加されます.この変数の値を変 更すると自動的にRubyの対応する変数の値も変わります. またRuby側からは更新できない変数もあります.このread onlyの 変数は以下の関数で定義します. void rb_define_readonly_variable(const char *name, VALUE *var) これら変数の他にhookをつけた大域変数を定義できます.hook付き の大域変数は以下の関数を用いて定義します.hook付き大域変数の 値の参照や設定はhookで行う必要があります. void rb_define_hooked_variable(const char *name, VALUE *var, VALUE (*getter)(), void (*setter)()) この関数はCの関数によってhookのつけられた大域変数を定義しま す.変数が参照された時には関数getterが,変数に値がセットされ た時には関数setterが呼ばれる.hookを指定しない場合はgetterや setterに0を指定します. # getterもsetterも0ならばrb_define_variable()と同じになる. getterとsetterの仕様は次の通りです。 VALUE (*getter)(ID id, VALUE *var); void (*setter)(VALUE val, ID id, VALUE *var); それから,対応するCの変数を持たないRubyの大域変数を定義する こともできます. その変数の値はフック関数のみによって取得・設定 されます. void rb_define_virtual_variable(const char *name, VALUE (*getter)(), void (*setter)()) この関数によって定義されたRubyの大域変数が参照された時には getterが,変数に値がセットされた時にはsetterが呼ばれます. getterとsetterの仕様は以下の通りです. (*getter)(ID id); (*setter)(VALUE val, ID id); 3.3 CのデータをRubyオブジェクトにする Cの世界で定義されたデータ(構造体)をRubyのオブジェクトとして 取り扱いたい場合がありえます.このような場合には,Dataという RubyオブジェクトにCの構造体(へのポインタ)をくるむことでRuby オブジェクトとして取り扱えるようになります. Dataオブジェクトを生成して構造体をRubyオブジェクトにカプセル 化するためには,以下のマクロを使います. Data_Wrap_Struct(klass, mark, free, ptr) このマクロの戻り値は生成されたDataオブジェクトです. klassはこのDataオブジェクトのクラスです.ptrはカプセル化する Cの構造体へのポインタです.markはこの構造体がRubyのオブジェ クトへの参照がある時に使う関数です.そのような参照を含まない 時には0を指定します. # そのような参照は勧められません. freeはこの構造体がもう不要になった時に呼ばれる関数です.この 関数がガーベージコレクタから呼ばれます.これが-1の場合は,単 純に開放されます. markおよびfree関数はGC実行中に呼び出されます. なお, GC実行中はRubyオブジェクトのアロケーションは禁止されま す. よって, markおよびfree関数でRubyオブジェクトのアロケーシ ョンは行わないでください. Cの構造体の割当とDataオブジェクトの生成を同時に行うマクロと して以下のものが提供されています. Data_Make_Struct(klass, type, mark, free, sval) このマクロの戻り値は生成されたDataオブジェクトです. klass, mark, freeはData_Wrap_Structと同じ働きをします.type は割り当てるC構造体の型です.割り当てられた構造体は変数sval に代入されます.この変数の型は (type*) である必要があります. Dataオブジェクトからポインタを取り出すのは以下のマクロを用い ます. Data_Get_Struct(obj, type, sval) Cの構造体へのポインタは変数svalに代入されます. これらのDataの使い方はちょっと分かりにくいので,後で説明する 例題を参照してください. 4.例題 - dbmパッケージを作る ここまでの説明でとりあえず拡張ライブラリは作れるはずです. Rubyのextディレクトリにすでに含まれているdbmライブラリを例に して段階的に説明します. (1) ディレクトリを作る % mkdir ext/dbm Ruby 1.1からは任意のディレクトリでダイナミックライブラリを作 ることができるようになりました.Rubyに静的にリンクする場合に はRubyを展開したディレクトリの下,extディレクトリの中に拡張 ライブラリ用のディレクトリを作る必要があります.名前は適当に 選んで構いません. (2) 設計する まあ,当然なんですけど,どういう機能を実現するかどうかまず設 計する必要があります.どんなクラスをつくるか,そのクラスには どんなメソッドがあるか,クラスが提供する定数などについて設計 します. (3) Cコードを書く 拡張ライブラリ本体となるC言語のソースを書きます.C言語のソー スがひとつの時には「ライブラリ名.c」を選ぶと良いでしょう.C 言語のソースが複数の場合には逆に「ライブラリ名.c」というファ イル名は避ける必要があります.オブジェクトファイルとモジュー ル生成時に中間的に生成される「ライブラリ名.o」というファイル とが衝突するからです. Rubyは拡張ライブラリをロードする時に「Init_ライブラリ名」と いう関数を自動的に実行します.dbmライブラリの場合「Init_dbm」 です.この関数の中でクラス,モジュール,メソッド,定数などの 定義を行います.dbm.cから一部引用します. -- void Init_dbm(void) { /* DBMクラスを定義する */ cDBM = rb_define_class("DBM", rb_cObject); /* DBMはEnumerateモジュールをインクルードする */ rb_include_module(cDBM, rb_mEnumerable); /* DBMクラスのクラスメソッドopen(): 引数はCの配列で受ける */ rb_define_singleton_method(cDBM, "open", fdbm_s_open, -1); /* DBMクラスのメソッドclose(): 引数はなし */ rb_define_method(cDBM, "close", fdbm_close, 0); /* DBMクラスのメソッド[]: 引数は1個 */ rb_define_method(cDBM, "[]", fdbm_fetch, 1); : /* DBMデータを格納するインスタンス変数名のためのID */ id_dbm = rb_intern("dbm"); } -- DBMライブラリはdbmのデータと対応するオブジェクトになるはずで すから,Cの世界のdbmをRubyの世界に取り込む必要があります. dbm.cではData_Make_Structを以下のように使っています. -- struct dbmdata { int di_size; DBM *di_dbm; }; obj = Data_Make_Struct(klass, struct dbmdata, 0, free_dbm, dbmp); -- ここではdbmstruct構造体へのポインタをDataにカプセル化してい ます.DBM*を直接カプセル化しないのはclose()した時の処理を考 えてのことです. Dataオブジェクトからdbmstruct構造体のポインタを取り出すため に以下のマクロを使っています. -- #define GetDBM(obj, dbmp) {\ Data_Get_Struct(obj, struct dbmdata, dbmp);\ if (dbmp->di_dbm == 0) closed_dbm();\ } -- ちょっと複雑なマクロですが,要するにdbmdata構造体のポインタ の取り出しと,closeされているかどうかのチェックをまとめてい るだけです. DBMクラスにはたくさんメソッドがありますが,分類すると3種類の 引数の受け方があります.ひとつは引数の数が固定のもので,例と してはdeleteメソッドがあります.deleteメソッドを実装している fdbm_delete()はこのようになっています. -- static VALUE fdbm_delete(VALUE obj, VALUE keystr) { : } -- 引数の数が固定のタイプは第1引数がself,第2引数以降がメソッド の引数となります. 引数の数が不定のものはCの配列で受けるものとRubyの配列で受け るものとがあります.dbmライブラリの中で,Cの配列で受けるもの はDBMのクラスメソッドであるopen()です.これを実装している関 数fdbm_s_open()はこうなっています. -- static VALUE fdbm_s_open(int argc, VALUE *argv, VALUE klass) { : if (rb_scan_args(argc, argv, "11", &file, &vmode) == 1) { mode = 0666; /* default value */ } : } -- このタイプの関数は第1引数が与えられた引数の数,第2引数が与え られた引数の入っている配列になります.selfは第3引数として与 えられます. この配列で与えられた引数を解析するための関数がopen()でも使わ れているrb_scan_args()です.第3引数に指定したフォーマットに 従い,第4変数以降に指定した変数に値を代入してくれます.この フォーマットは,第1文字目が省略できない引数の数,第2文字目が 省略できる引数の数,第3文字目が対応する相手が無いあまりの引 数があるかどうかを示す"*"です.2文字目と3文字目は省略できま す.dbm.cの例では,フォーマットは"11"ですから,引数は最低1つ で,2つまで許されるという意味になります.省略されている時の 変数の値はnil(C言語のレベルではQnil)になります. Rubyの配列で引数を受け取るものはindexesがあります.実装はこ うです. -- static VALUE fdbm_indexes(VALUE obj, VALUE args) { : } -- 第1引数はself,第2引数はRubyの配列です. ** 注意事項 Rubyと共有はしないがRubyのオブジェクトを格納する可能性のある Cの大域変数は以下の関数を使ってRubyインタプリタに変数の存在 を教えてあげてください.でないとGCでトラブルを起こします. void rb_global_variable(VALUE *var) (4) extconf.rbを用意する Makefileを作る場合の雛型になるextconf.rbというファイルを作り ます.extconf.rbはライブラリのコンパイルに必要な条件のチェッ クなどを行うことが目的です.まず, require 'mkmf' をextconf.rbの先頭に置きます.extconf.rbの中では以下のRuby関 数を使うことが出来ます. have_library(lib, func): ライブラリの存在チェック have_func(func, header): 関数の存在チェック have_header(header): ヘッダファイルの存在チェック create_makefile(target): Makefileの生成 以下の変数を使うことができます. $CFLAGS: コンパイル時に追加的に指定するフラグ(-Oなど) $CPPFLAGS: プリプロセッサに追加的に指定するフラグ(-Iや-Dなど) $LDFLAGS: リンク時に追加的に指定するフラグ(-Lなど) $objs: リンクされるオブジェクトファイル名のリスト オブジェクトファイルのリストは,通常はソースファイルを検索し て自動的に生成されますが,makeの途中でソースを生成するような 場合は明示的に指定する必要があります. ライブラリをコンパイルする条件が揃わず,そのライブラリをコン パイルしない時にはcreate_makefileを呼ばなければMakefileは生 成されず,コンパイルも行われません. (5) dependを用意する もし,ディレクトリにdependというファイルが存在すれば, Makefileが依存関係をチェックしてくれます. % gcc -MM *.c > depend などで作ることが出来ます.あって損は無いでしょう. (6) Makefileを生成する Makefileを実際に生成するためには ruby extconf.rb とします.extconf.rbに require 'mkmf' の行がない場合にはエラー になりますので,引数を追加して ruby -r mkmf extconf.rb としてください. site_ruby ディレクトリでなく, vendor_ruby ディレクトリにインストールする場合には 以下のように --vendor オプションを加えてください. ruby extconf.rb --vendor ディレクトリをext以下に用意した場合にはRuby全体のmakeの時に 自動的にMakefileが生成されますので,このステップは不要です. (7) makeする 動的リンクライブラリを生成する場合にはその場でmakeしてくださ い.必要であれば make install でインストールされます. ext以下にディレクトリを用意した場合は,Rubyのディレクトリで makeを実行するとMakefileを生成からmake,必要によってはそのモ ジュールのRubyへのリンクまで自動的に実行してくれます. extconf.rbを書き換えるなどしてMakefileの再生成が必要な時はま たRubyディレクトリでmakeしてください. 拡張ライブラリはmake installでRubyライブラリのディレクトリの 下にコピーされます.もし拡張ライブラリと協調して使うRubyで記 述されたプログラムがあり,Rubyライブラリに置きたい場合には, 拡張ライブラリ用のディレクトリの下に lib というディレクトリ を作り,そこに 拡張子 .rb のファイルを置いておけば同時にイン ストールされます. (8) デバッグ まあ,デバッグしないと動かないでしょうね.ext/Setupにディレ クトリ名を書くと静的にリンクするのでデバッガが使えるようにな ります.その分コンパイルが遅くなりますけど. (9) できあがり 後はこっそり使うなり,広く公開するなり,売るなり,ご自由にお 使いください.Rubyの作者は拡張ライブラリに関して一切の権利を 主張しません. Appendix A. Rubyのソースコードの分類 Rubyのソースはいくつかに分類することが出来ます.このうちクラ スライブラリの部分は基本的に拡張ライブラリと同じ作り方になっ ています.これらのソースは今までの説明でほとんど理解できると 思います. Ruby言語のコア class.c : クラスとモジュール error.c : 例外クラスと例外機構 gc.c : 記憶領域管理 load.c : ライブラリのロード object.c : オブジェクト variable.c : 変数と定数 Rubyの構文解析器 parse.y : 字句解析器と構文定義 -> parse.c : 自動生成 keywords : 予約語 -> lex.c : 自動生成 Rubyの評価器(通称YARV) blockinlining.c compile.c eval.c eval_error.c eval_jump.c eval_safe.c insns.def : 仮想機械語の定義 iseq.c : VM::ISeqの実装 thread.c : スレッド管理とコンテキスト切り替え thread_win32.c : スレッド実装 thread_pthread.c : 同上 vm.c vm_dump.c vm_eval.c vm_evalbody.c vm_insnhelper.c vm_method.c opt_insns_unif.def : 命令融合 opt_operand.def : 最適化のための定義 -> insn*.inc : 自動生成 -> opt*.inc : 自動生成 -> vm.inc : 自動生成 正規表現エンジン (鬼車) regex.c regcomp.c regenc.c regerror.c regexec.c regparse.c regsyntax.c ユーティリティ関数 debug.c : Cデバッガ用のデバッグシンボル dln.c : 動的ローディング st.c : 汎用ハッシュ表 strftime.c : 時刻整形 util.c : その他のユーティリティ Rubyコマンドの実装 dmyext.c dmydln.c dmyencoding.c id.c inits.c main.c ruby.c version.c gem_prelude.rb prelude.rb クラスライブラリ array.c : Array bignum.c : Bignum compar.c : Comparable complex.c : Complex cont.c : Fiber, Continuation dir.c : Dir enum.c : Enumerable enumerator.c : Enumerator file.c : File hash.c : Hash io.c : IO marshal.c : Marshal math.c : Math numeric.c : Numeric, Integer, Fixnum, Float pack.c : Array#pack, String#unpack prec.c : Precision proc.c : Binding, Proc process.c : Process random.c : random number range.c : Range rational.c : Rational re.c : Regexp, MatchData signal.c : Signal sprintf.c : string.c : String struct.c : Struct time.c : Time 多言語化 encoding.c : Encoding transcode.c : Encoding::Converter enc/*.c : エンコーディングクラス群 enc/trans/* : コードポイント対応表 gorubyコマンドの実装 goruby.c golf_prelude.rb Appendix B. 拡張用関数リファレンス C言語からRubyの機能を利用するAPIは以下の通りである. ** 型 VALUE Rubyオブジェクトを表現する型.必要に応じてキャストして用いる. 組み込み型を表現するCの型はruby.hに記述してあるRで始まる構造 体である.VALUE型をこれらにキャストするためにRで始まる構造体 名を全て大文字にした名前のマクロが用意されている. ** 変数・定数 Qnil 定数: nilオブジェクト Qtrue 定数: trueオブジェクト(真のデフォルト値) Qfalse 定数: falseオブジェクト ** Cデータのカプセル化 Data_Wrap_Struct(VALUE klass, void (*mark)(), void (*free)(), void *sval) Cの任意のポインタをカプセル化したRubyオブジェクトを返す.こ のポインタがRubyからアクセスされなくなった時,freeで指定した 関数が呼ばれる.また,このポインタの指すデータが他のRubyオブ ジェクトを指している場合,markに指定する関数でマークする必要 がある. Data_Make_Struct(klass, type, mark, free, sval) type型のメモリをmallocし,変数svalに代入した後,それをカプセ ル化したデータを返すマクロ. Data_Get_Struct(data, type, sval) dataからtype型のポインタを取り出し変数svalに代入するマクロ. ** 型チェック TYPE(value) FIXNUM_P(value) NIL_P(value) void Check_Type(VALUE value, int type) void Check_SafeStr(VALUE value) ** 型変換 FIX2INT(value) FIX2LONG(value) INT2FIX(i) NUM2INT(value) NUM2LONG(value) INT2NUM(i) NUM2DBL(value) rb_float_new(f) StringValue(value) StringValuePtr(value) StringValueCStr(value) rb_str_new2(s) ** クラス/モジュール定義 VALUE rb_define_class(const char *name, VALUE super) superのサブクラスとして新しいRubyクラスを定義する. VALUE rb_define_class_under(VALUE module, const char *name, VALUE super) superのサブクラスとして新しいRubyクラスを定義し,moduleの 定数として定義する. VALUE rb_define_module(const char *name) 新しいRubyモジュールを定義する. VALUE rb_define_module_under(VALUE module, const char *name) 新しいRubyモジュールを定義し,moduleの定数として定義する. void rb_include_module(VALUE klass, VALUE module) モジュールをインクルードする.classがすでにmoduleをインク ルードしている時には何もしない(多重インクルードの禁止). void rb_extend_object(VALUE object, VALUE module) オブジェクトをモジュール(で定義されているメソッド)で拡張する. ** 大域変数定義 void rb_define_variable(const char *name, VALUE *var) RubyとCとで共有するグローバル変数を定義する.変数名が`$'で 始まらない時には自動的に追加される.nameとしてRubyの識別子 として許されない文字(例えば` ')を含む場合にはRubyプログラ ムからは見えなくなる. void rb_define_readonly_variable(const char *name, VALUE *var) RubyとCとで共有するread onlyのグローバル変数を定義する. read onlyであること以外はrb_define_variable()と同じ. void rb_define_virtual_variable(const char *name, VALUE (*getter)(), void (*setter)()) 関数によって実現されるRuby変数を定義する.変数が参照された 時にはgetterが,変数に値がセットされた時にはsetterが呼ばれ る. void rb_define_hooked_variable(const char *name, VALUE *var, VALUE (*getter)(), void (*setter)()) 関数によってhookのつけられたグローバル変数を定義する.変数 が参照された時にはgetterが,関数に値がセットされた時には setterが呼ばれる.getterやsetterに0を指定した時にはhookを 指定しないのと同じ事になる. void rb_global_variable(VALUE *var) GCのため,Rubyプログラムからはアクセスされないが, Rubyオブ ジェクトを含む大域変数をマークする. ** 定数 void rb_define_const(VALUE klass, const char *name, VALUE val) 定数を定義する. void rb_define_global_const(const char *name, VALUE val) 大域定数を定義する. rb_define_const(rb_cObject, name, val) と同じ意味. ** メソッド定義 rb_define_method(VALUE klass, const char *name, VALUE (*func)(), int argc) メソッドを定義する.argcはselfを除く引数の数.argcが-1の時, 関数には引数の数(selfを含まない)を第1引数, 引数の配列を第2 引数とする形式で与えられる(第3引数はself).argcが-2の時, 第1引数がself, 第2引数がargs(argsは引数を含むRubyの配列)と いう形式で与えられる. rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(), int argc) privateメソッドを定義する.引数はrb_define_method()と同じ. rb_define_singleton_method(VALUE klass, const char *name, VALUE (*func)(), int argc) 特異メソッドを定義する.引数はrb_define_method()と同じ. rb_scan_args(int argc, VALUE *argv, const char *fmt, ...) argc, argv形式で与えられた引数を分解する.fmtは必須引数の数, 付加引数の数, 残りの引数があるかを指定する文字列で, "数字 数字*"という形式である. 2 番目の数字と"*"はそれぞれ省略可 能である.必須引数が一つもない場合は0を指定する.第3引数以 降は変数へのポインタで, 該当する要素がその変数に格納される. 付加引数に対応する引数が与えられていない場合は変数にQnilが 代入される. ** Rubyメソッド呼び出し VALUE rb_funcall(VALUE recv, ID mid, int narg, ...) メソッド呼び出し.文字列からmidを得るためにはrb_intern()を 使う. VALUE rb_funcall2(VALUE recv, ID mid, int argc, VALUE *argv) メソッド呼び出し.引数をargc, argv形式で渡す. VALUE rb_eval_string(const char *str) 文字列をRubyスクリプトとしてコンパイル・実行する. ID rb_intern(const char *name) 文字列に対応するIDを返す. char *rb_id2name(ID id) IDに対応する文字列を返す(デバッグ用). char *rb_class2name(VALUE klass) クラスの名前を返す(デバッグ用).クラスが名前を持たない時に は, 祖先を遡って名前を持つクラスの名前を返す. int rb_respond_to(VALUE obj, ID id) objがidで示されるメソッドを持つかどうかを返す. ** インスタンス変数 VALUE rb_iv_get(VALUE obj, const char *name) objのインスタンス変数の値を得る.`@'で始まらないインスタン ス変数は Rubyプログラムからアクセスできない「隠れた」イン スタンス変数になる.定数は大文字の名前を持つクラス(または モジュール)のインスタンス変数として実装されている. VALUE rb_iv_set(VALUE obj, const char *name, VALUE val) objのインスタンス変数をvalにセットする. ** 制御構造 VALUE rb_iterate(VALUE (*func1)(), VALUE arg1, VALUE (*func2)(), VALUE arg2) func2をブロックとして設定し, func1をイテレータとして呼ぶ. func1には arg1が引数として渡され, func2には第1引数にイテレー タから与えられた値, 第2引数にarg2が渡される. VALUE rb_yield(VALUE val) valを値としてイテレータブロックを呼び出す. VALUE rb_rescue(VALUE (*func1)(), VALUE arg1, VALUE (*func2)(), VALUE arg2) 関数func1をarg1を引数に呼び出す.func1の実行中に例外が発生 した時には func2をarg2を引数として呼ぶ.戻り値は例外が発生 しなかった時はfunc1の戻り値, 例外が発生した時にはfunc2の戻 り値である. VALUE rb_ensure(VALUE (*func1)(), VALUE arg1, void (*func2)(), VALUE arg2) 関数func1をarg1を引数として実行し, 実行終了後(たとえ例外が 発生しても) func2をarg2を引数として実行する.戻り値はfunc1 の戻り値である(例外が発生した時は戻らない). ** 例外・エラー void rb_warning(const char *fmt, ...) rb_verbose時に標準エラー出力に警告情報を表示する.引数は printf()と同じ. void rb_raise(rb_eRuntimeError, const char *fmt, ...) RuntimeError例外を発生させる.引数はprintf()と同じ. void rb_raise(VALUE exception, const char *fmt, ...) exceptionで指定した例外を発生させる.fmt以下の引数は printf()と同じ. void rb_fatal(const char *fmt, ...) 致命的例外を発生させる.通常の例外処理は行なわれず, インター プリタが終了する(ただしensureで指定されたコードは終了前に 実行される). void rb_bug(const char *fmt, ...) インタープリタなどプログラムのバグでしか発生するはずのない 状況の時呼ぶ.インタープリタはコアダンプし直ちに終了する. 例外処理は一切行なわれない. ** Rubyの初期化・実行 Rubyをアプリケーションに埋め込む場合には以下のインタフェース を使う.通常の拡張ライブラリには必要ない. void ruby_init() Rubyインタプリタの初期化を行なう. void ruby_options(int argc, char **argv) Rubyインタプリタのコマンドライン引数の処理を行なう. void ruby_run() Rubyインタプリタを実行する. void ruby_script(char *name) Rubyのスクリプト名($0)を設定する. ** インタプリタのイベントのフック void rb_add_event_hook(rb_event_hook_func_t func, rb_event_t events) 指定されたインタプリタのイベントに対するフック関数を追加します. eventsは以下の値のorでなければなりません: RUBY_EVENT_LINE RUBY_EVENT_CLASS RUBY_EVENT_END RUBY_EVENT_CALL RUBY_EVENT_RETURN RUBY_EVENT_C_CALL RUBY_EVENT_C_RETURN RUBY_EVENT_RAISE RUBY_EVENT_ALL rb_event_hook_func_tの定義は以下の通りです: typedef void (*rb_event_hook_func_t)(rb_event_t event, NODE *node, VALUE self, ID id, VALUE klass) int rb_remove_event_hook(rb_event_hook_func_t func) 指定されたフック関数を削除します. Appendix C. extconf.rbで使える関数たち extconf.rbの中では利用可能なコンパイル条件チェックの関数は以 下の通りである. have_macro(macro, headers) ヘッダファイルheaderをインクルードしてマクロmacroが定義さ れているかどうかチェックする.マクロが定義されている時true を返す. have_library(lib, func) 関数funcを定義しているライブラリlibの存在をチェックする. ライブラリが存在する時,trueを返す. find_library(lib, func, path...) 関数funcを定義しているライブラリlibの存在を -Lpath を追加 しながらチェックする.ライブラリが見付かった時,trueを返す. have_func(func, header) ヘッダファイルheaderをインクルードして関数funcの存在をチェ ックする.funcが標準ではリンクされないライブラリ内のもので ある時には先にhave_libraryでそのライブラリをチェックしてお く事.関数が存在する時trueを返す. have_var(var, header) ヘッダファイルheaderをインクルードして変数varの存在をチェッ クする.varが標準ではリンクされないライブラリ内のものであ る時には先にhave_libraryでそのライブラリをチェックしておく 事.変数が存在する時trueを返す. have_header(header) ヘッダファイルの存在をチェックする.ヘッダファイルが存在す る時trueを返す. find_header(header, path...) ヘッダファイルheaderの存在を -Ipath を追加しながらチェック する.ヘッダファイルが見付かった時,trueを返す. have_struct_member(type, member, header) ヘッダファイルheaderをインクルードして型typeにメンバmember が存在するかをチェックする.typeが定義されていて,memberを 持つする時trueを返す. have_type(type, header, opt) ヘッダファイルheaderをインクルードして型typeが存在するかを チェックする.typeが定義されている時trueを返す. check_sizeof(type, header) ヘッダファイルheaderをインクルードして型typeのchar単位サイ ズを調べる.typeが定義されている時そのサイズを返す.定義さ れていないときはnilを返す. create_makefile(target) 拡張ライブラリ用のMakefileを生成する.この関数を呼ばなけれ ばそのライブラリはコンパイルされない.targetはモジュール名 を表す. find_executable(command, path) コマンドcommandをFile::PATH_SEPARATORで区切られたパス名の リストpathから探す.pathがnilまたは省略された場合は,環境 変数PATHの値を使用する.実行可能なコマンドが見つかった場合 はパスを含むファイル名,見つからなかった場合はnilを返す. with_config(withval[, default=nil]) コマンドライン上の--with-で指定されたオプション値 を得る. enable_config(config, *defaults) disable_config(config, *defaults) コマンドライン上の--enable-または --disable-で指定された真偽値を得る. --enable-が指定されていた場合はtrue, --disable-が指定されていた場合はfalseを返す. どちらも指定されていない場合は,ブロックつきで呼び出されて いる場合は*defaultsをyieldした結果,ブロックなしなら *defaultsを返す. dir_config(target[, default_dir]) dir_config(target[, default_include, default_lib]) コマンドライン上の--with--dir, --with--include, --with--libのいずれかで指定されるディレクトリを $CFLAGS や $LDFLAGS に追加する.--with--dir=/pathは --with--include=/path/include --with--lib=/path/lib と等価である.追加された include ディレクトリと lib ディレ クトリの配列を返す. ([include_dir, lib_dir]) pkg_config(pkg) pkg-configコマンドからパッケージpkgの情報を得る. pkg-configの実際のコマンド名は,--with-pkg-configコマンド ラインオプションで指定可能. /* * Local variables: * fill-column: 60 * end: */