[ruby-list:75] Ruby extension howto (sono 1)
From:
matz@... (Yukihiro Matsumoto)
Date:
1996-01-22 05:32:00 UTC
List:
ruby-list #75
まつもと ゆきひろ@トヨタケーラムです.
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引数は必ず
配列でなければならない.