[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引数は必ず
    配列でなければならない.

In This Thread

Prev Next