From: matz@... (Yukihiro Matsumoto) Date: 1996-01-22T14:32:00+09:00 Subject: [ruby-list:75] Ruby extension howto (sono 1) まつもと ゆきひろ@トヨタケーラムです. ruby拡張モジュール作成講座の1回目です.今回はrubyのデータ構 造がどのようにCで記述されているかという「基礎知識編」です. ちょっとややこしいですが,我慢してください.そのうち,実際に 拡張モジュールを作る話が出来ると思います(その2またはその3?). -- ruby拡張モジュール作成講座 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_FIXNUM Fixnum(31bit長整数) T_HASH 連想配列 T_STRUCT (rubyの)構造体 T_BIGNUM 多倍長整数 T_TRUE 真 T_FALSE 偽 T_DATA データ その他に内部で利用されている以下のタイプがあります. T_ICLASS T_MATCH T_VARMAP T_SCOPE T_NODE いくつかのタイプは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: /* 例外を発生させる */ Fail("not valid value"); break; } それとデータタイプをチェックして,正しくなければ例外を発生す る関数が用意されています. void Check_Type(VALUE value, int type) この関数はvalueがtypeで無ければ,例外を発生させます.引数と して与えられたVALUEのデータタイプが正しいかどうかチェックす るためには,この関数を使います. 1.3 VALUEをCのデータに変換する データタイプがT_NIL, T_FALSE, T_TRUEである時,データはそれぞ れnil, FALSE, TRUEです.このデータタイプのオブジェクトはひと つずつしか存在しません. データタイプがT_FIXNUMの時,これは31bitのサイズを持つ整数で す.FIXNUMをCの整数に変換するためにはマクロ「FIX2INT()」を使 います.それから,FIXNUMに限らずrubyのデータを整数に変換する 「NUM2INT()」というマクロがあります.このマクロはデータタイ プのチェック無しで使えます(整数に変換できない場合には例外が 発生する). それ以外のデータタイプは対応するCの構造体があります.対応す る構造体のあるVALUEはそのままキャスト(型変換)すれば構造体の ポインタに変換できます. 構造体は「struct RXxxxx」という名前でruby.hで定義されていま す.例えば文字列は「struct RString」です.実際に使う可能性が あるのは文字列と配列くらいだと思います. ruby.hでは構造体へキャストするマクロも「RXXXXX()」(全部大文 字にしたもの)という名前で提供されています(例: RSTRING()). 例えば,文字列strの長さを得るためには「RSTRING(str)->len」と し,文字列strをchar*として得るためには「RSTRING(str)->ptr」 とします.配列の場合には,それぞれ「RARRAT(str)->len」, 「RARRAT(str)->ptr」となります. 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以内に収まる時 INT2NUM() 任意の整数からVALUEへ INT2NUM()は整数がFIXNUMの範囲に収まらない場合,Bignumに変換 してくれます(が,少し遅い). 1.5 rubyのデータを操作する 先程も述べた通り,rubyの構造体をアクセスする時に内容の更新を 行うことは勧められません.で,rubyのデータを操作する時には rubyが用意している関数を用いてください. ここではもっとも使われるであろう文字列と配列の生成/操作を行 い関数をあげます(全部ではないです). 文字列に対する関数 str_new(char *ptr, int len) 新しいrubyの文字列を生成する. str_new2(char *ptr) Cの文字列からrubyの文字列を生成する.この関数の機能は str_new(ptr, strlen(ptr))と同等である. str_cat(VALUE str, char *ptr, int len) rubyの文字列strにlenバイトの文字列ptrを追加する. 配列に対する関数 ary_new() 要素が0の配列を生成する. ary_new2(int len) 要素が0の配列を生成する.len要素分の領域をあらかじめ割り 当てておく. ary_new3(int n, ...) 引数で指定したn要素を含む配列を生成する. ary_new4(int n, VALUE elts[]) 配列で与えたn要素の配列を生成する. ary_push(VALUE ary) ary_pop(VALUE ary, VALUE val) ary_shift(VALUE ary) ary_unshift(VALUE ary, VALUE val) ary_entry(VALUE ary, int idx) Arrayの同名のメソッドと同じ働きをする関数.第1引数は必ず 配列でなければならない.