зеркало из https://github.com/github/ruby.git
1189 строки
32 KiB
Plaintext
1189 строки
32 KiB
Plaintext
.\" README.EXT - -*- 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_FIXNUM Fixnum(31bit長整数)
|
||
T_HASH 連想配列
|
||
T_STRUCT (Rubyの)構造体
|
||
T_BIGNUM 多倍長整数
|
||
T_FILE 入出力
|
||
T_TRUE 真
|
||
T_FALSE 偽
|
||
T_DATA データ
|
||
T_SYMBOL シンボル
|
||
|
||
その他に内部で利用されている以下のタイプがあります.
|
||
|
||
T_ICLASS
|
||
T_MATCH
|
||
T_UNDEF
|
||
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:
|
||
/* 例外を発生させる */
|
||
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のサイズを持つ整数で
|
||
す.FIXNUMをCの整数に変換するためにはマクロ「FIX2INT()」を使
|
||
います.それから,FIXNUMに限らずRubyのデータを整数に変換する
|
||
「NUM2INT()」というマクロがあります.このマクロはデータタイ
|
||
プのチェック無しで使えます(整数に変換できない場合には例外が
|
||
発生する).
|
||
|
||
同様にチェック無しで使える変換マクロはdoubleを取り出す
|
||
「NUM2DBL()」とchar*を取り出す「STR2CSTR()」があります.
|
||
|
||
それ以外のデータタイプは対応するCの構造体があります.対応す
|
||
る構造体のあるVALUEはそのままキャスト(型変換)すれば構造体の
|
||
ポインタに変換できます.
|
||
|
||
構造体は「struct RXxxxx」という名前でruby.hで定義されていま
|
||
す.例えば文字列は「struct RString」です.実際に使う可能性が
|
||
あるのは文字列と配列くらいだと思います.
|
||
|
||
ruby.hでは構造体へキャストするマクロも「RXXXXX()」(全部大文
|
||
字にしたもの)という名前で提供されています(例: RSTRING()).
|
||
|
||
例えば,文字列strの長さを得るためには「RSTRING(str)->len」と
|
||
し,文字列strをchar*として得るためには「RSTRING(str)->ptr」
|
||
とします.配列の場合には,それぞれ「RARRAY(ary)->len」,
|
||
「RARRAY(ary)->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が用意している関数を用いてください.
|
||
|
||
ここではもっとも使われるであろう文字列と配列の生成/操作を行
|
||
い関数をあげます(全部ではないです).
|
||
|
||
文字列に対する関数
|
||
|
||
rb_str_new(const char *ptr, long len)
|
||
|
||
新しいRubyの文字列を生成する.
|
||
|
||
rb_str_new2(const char *ptr)
|
||
|
||
Cの文字列からRubyの文字列を生成する.この関数の機能は
|
||
rb_str_new(ptr, strlen(ptr))と同等である.
|
||
|
||
rb_tainted_str_new(const char *ptr, long len)
|
||
|
||
汚染マークが付加された新しいRubyの文字列を生成する.外部
|
||
からのデータに基づく文字列には汚染マークが付加されるべき
|
||
である.
|
||
|
||
rb_tainted_str_new2(const char *ptr)
|
||
|
||
Cの文字列から汚染マークが付加されたRubyの文字列を生成する.
|
||
|
||
rb_str_cat(VALUE str, const char *ptr, long len)
|
||
|
||
Rubyの文字列strにlenバイトの文字列ptrを追加する.
|
||
|
||
配列に対する関数
|
||
|
||
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_push(VALUE ary, VALUE val)
|
||
rb_ary_pop(VALUE ary)
|
||
rb_ary_shift(VALUE ary)
|
||
rb_ary_unshift(VALUE ary, VALUE val)
|
||
|
||
Arrayの同名のメソッドと同じ働きをする関数.第1引数は必ず
|
||
配列でなければならない.
|
||
|
||
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の配列として渡されます.
|
||
|
||
メソッドを定義する関数はもう二つあります.ひとつはprivateメ
|
||
ソッドを定義する関数で,引数はrb_define_method()と同じです.
|
||
|
||
void rb_define_private_method(VALUE klass, const char *name,
|
||
VALUE (*func)(), int argc)
|
||
|
||
privateメソッドとは関数形式でしか呼び出すことの出来ないメソッ
|
||
ドです.
|
||
|
||
もうひとつはモジュール関数を定義するものです.モジュール関数
|
||
とはモジュールの特異メソッドであり,同時に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);
|
||
|
||
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)
|
||
|
||
この評価は現在の環境で行われます.つまり,現在のローカル変数
|
||
などを受け継ぎます.
|
||
|
||
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()と同じになる.
|
||
|
||
それから,Cの関数によって実現されるRubyの大域変数を定義する
|
||
関数があります.
|
||
|
||
void rb_define_virtual_variable(const char *name,
|
||
VALUE (*getter)(), void (*setter)())
|
||
|
||
この関数によって定義されたRubyの大域変数が参照された時には
|
||
getterが,変数に値がセットされた時にはsetterが呼ばれます.
|
||
|
||
getterとsetterの仕様は以下の通りです.
|
||
|
||
(*getter)(ID id, void *data, struct global_entry* entry);
|
||
(*setter)(VALUE val, ID id, void *data, struct global_entry* entry);
|
||
|
||
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はこの構造体がもう不要になった時に呼ばれる関数です.この
|
||
関数がガーベージコレクタから呼ばれます.
|
||
|
||
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) MANIFESTファイルを作る
|
||
|
||
% cd ext/dbm
|
||
% touch MANIFEST
|
||
|
||
拡張ライブラリのディレクトリの下にはMANIFESTというファイルが
|
||
必要なので,とりあえず空のファイルを作っておきます.後でこの
|
||
ファイルには必要なファイル一覧が入ることになります.
|
||
|
||
MANIFESTというファイルは,静的リンクのmakeの時にディレクトリ
|
||
が拡張ライブラリを含んでいるかどうか判定するために使われれて
|
||
います.ダイナミックライブラリを作る場合には必ずしも必要では
|
||
ありません.
|
||
|
||
(3) 設計する
|
||
|
||
まあ,当然なんですけど,どういう機能を実現するかどうかまず設
|
||
計する必要があります.どんなクラスをつくるか,そのクラスには
|
||
どんなメソッドがあるか,クラスが提供する定数などについて設計
|
||
します.
|
||
|
||
(4) Cコードを書く
|
||
|
||
拡張ライブラリ本体となるC言語のソースを書きます.C言語のソー
|
||
スがひとつの時には「ライブラリ名.c」を選ぶと良いでしょう.C
|
||
言語のソースが複数の場合には逆に「ライブラリ名.c」というファ
|
||
イル名は避ける必要があります.オブジェクトファイルとモジュー
|
||
ル生成時に中間的に生成される「ライブラリ名.o」というファイル
|
||
とが衝突するからです.
|
||
|
||
Rubyは拡張ライブラリをロードする時に「Init_ライブラリ名」と
|
||
いう関数を自動的に実行します.dbmライブラリの場合「Init_dbm」
|
||
です.この関数の中でクラス,モジュール,メソッド,定数などの
|
||
定義を行います.dbm.cから一部引用します.
|
||
|
||
--
|
||
Init_dbm()
|
||
{
|
||
/* 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(obj, keystr)
|
||
VALUE obj, keystr;
|
||
{
|
||
:
|
||
}
|
||
--
|
||
|
||
引数の数が固定のタイプは第1引数がself,第2引数以降がメソッド
|
||
の引数となります.
|
||
|
||
引数の数が不定のものはCの配列で受けるものとRubyの配列で受け
|
||
るものとがあります.dbmライブラリの中で,Cの配列で受けるもの
|
||
はDBMのクラスメソッドであるopen()です.これを実装している関
|
||
数fdbm_s_open()はこうなっています.
|
||
|
||
--
|
||
static VALUE
|
||
fdbm_s_open(argc, argv, klass)
|
||
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(obj, args)
|
||
VALUE obj, args;
|
||
{
|
||
:
|
||
}
|
||
--
|
||
|
||
第1引数はself,第2引数はRubyの配列です.
|
||
|
||
** 注意事項
|
||
|
||
Rubyと共有はしないがRubyのオブジェクトを格納する可能性のある
|
||
Cの大域変数は以下の関数を使ってRubyインタプリタに変数の存在
|
||
を教えてあげてください.でないとGCでトラブルを起こします.
|
||
|
||
void rb_global_variable(VALUE *var)
|
||
|
||
(5) 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: コンパイル時に追加的に指定するフラグ(-Iなど)
|
||
$LDFLAGS: リンク時に追加的に指定するフラグ(-Lなど)
|
||
|
||
ライブラリをコンパイルする条件が揃わず,そのライブラリをコン
|
||
パイルしない時にはcreate_makefileを呼ばなければMakefileは生
|
||
成されず,コンパイルも行われません.
|
||
|
||
(6) dependを用意する
|
||
|
||
もし,ディレクトリにdependというファイルが存在すれば,
|
||
Makefileが依存関係をチェックしてくれます.
|
||
|
||
% gcc -MM *.c > depend
|
||
|
||
などで作ることが出来ます.あって損は無いでしょう.
|
||
|
||
(7) MANIFESTファイルにファイル名を入れる
|
||
|
||
% find * -type f -print > MANIFEST
|
||
% vi MANIFEST
|
||
|
||
*.o, *~など不必要なファイル以外はMANIFESTに追加しておきます.
|
||
make時にはMANIFESTの内容は参照しませんので,空のままでも問題
|
||
は起きませんが,パッケージングの時に参照することがあるのと,
|
||
必要なファイルを区別できるので,用意しておいた方が良いでしょ
|
||
う.
|
||
|
||
(8) Makefileを生成する
|
||
|
||
Makefileを実際に生成するためには
|
||
|
||
ruby extconf.rb
|
||
|
||
とします.extconf.rbに require 'mkmf' の行がない場合にはエラー
|
||
になりますので,引数を追加して
|
||
|
||
ruby -r mkmf extconf.rb
|
||
|
||
としてください.
|
||
|
||
ディレクトリをext以下に用意した場合にはRuby全体のmakeの時に
|
||
自動的にMakefileが生成されますので,このステップは不要です.
|
||
|
||
(9) makeする
|
||
|
||
動的リンクライブラリを生成する場合にはその場でmakeしてくださ
|
||
い.必要であれば make install でインストールされます.
|
||
|
||
ext以下にディレクトリを用意した場合は,Rubyのディレクトリで
|
||
makeを実行するとMakefileを生成からmake,必要によってはそのモ
|
||
ジュールのRubyへのリンクまで自動的に実行してくれます.
|
||
extconf.rbを書き換えるなどしてMakefileの再生成が必要な時はま
|
||
たRubyディレクトリでmakeしてください.
|
||
|
||
拡張ライブラリはmake installでRubyライブラリのディレクトリの
|
||
下にコピーされます.もし拡張ライブラリと協調して使うRubyで記
|
||
述されたプログラムがあり,Rubyライブラリに置きたい場合には,
|
||
拡張ライブラリ用のディレクトリの下に lib というディレクトリ
|
||
を作り,そこに 拡張子 .rb のファイルを置いておけば同時にイン
|
||
ストールされます.
|
||
|
||
(10) デバッグ
|
||
|
||
まあ,デバッグしないと動かないでしょうね.ext/Setupにディレ
|
||
クトリ名を書くと静的にリンクするのでデバッガが使えるようにな
|
||
ります.その分コンパイルが遅くなりますけど.
|
||
|
||
(11) できあがり
|
||
|
||
後はこっそり使うなり,広く公開するなり,売るなり,ご自由にお
|
||
使いください.Rubyの作者は拡張ライブラリに関して一切の権利を
|
||
主張しません.
|
||
|
||
Appendix A. Rubyのソースコードの分類
|
||
|
||
Rubyのソースはいくつかに分類することが出来ます.このうちクラ
|
||
スライブラリの部分は基本的に拡張ライブラリと同じ作り方になっ
|
||
ています.これらのソースは今までの説明でほとんど理解できると
|
||
思います.
|
||
|
||
Ruby言語のコア
|
||
|
||
class.c
|
||
error.c
|
||
eval.c
|
||
gc.c
|
||
object.c
|
||
parse.y
|
||
variable.c
|
||
|
||
ユーティリティ関数
|
||
|
||
dln.c
|
||
regex.c
|
||
st.c
|
||
util.c
|
||
|
||
Rubyコマンドの実装
|
||
|
||
dmyext.c
|
||
inits.c
|
||
main.c
|
||
ruby.c
|
||
version.c
|
||
|
||
クラスライブラリ
|
||
|
||
array.c
|
||
bignum.c
|
||
compar.c
|
||
dir.c
|
||
enum.c
|
||
file.c
|
||
hash.c
|
||
io.c
|
||
marshal.c
|
||
math.c
|
||
numeric.c
|
||
pack.c
|
||
prec.c
|
||
process.c
|
||
random.c
|
||
range.c
|
||
re.c
|
||
signal.c
|
||
sprintf.c
|
||
string.c
|
||
struct.c
|
||
time.c
|
||
|
||
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)
|
||
INT2FIX(i)
|
||
NUM2INT(value)
|
||
INT2NUM(i)
|
||
NUM2DBL(value)
|
||
rb_float_new(f)
|
||
STR2CSTR(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, VALUE super)
|
||
|
||
新しい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)を設定する.
|
||
|
||
|
||
Appendix B. extconf.rbで使える関数たち
|
||
|
||
extconf.rbの中では利用可能なコンパイル条件チェックの関数は以
|
||
下の通りである.
|
||
|
||
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_header(header)
|
||
|
||
ヘッダファイルの存在をチェックする.ヘッダファイルが存在す
|
||
る時trueを返す.
|
||
|
||
find_header(header)
|
||
|
||
ヘッダファイルの存在を -Ipath を追加しながらチェックする.
|
||
ヘッダファイルが見付かった時trueを返す.
|
||
|
||
create_makefile(target)
|
||
|
||
拡張ライブラリ用のMakefileを生成する.この関数を呼ばなけれ
|
||
ばそのライブラリはコンパイルされない.targetはモジュール名
|
||
を表す.
|
||
|
||
with_config(withval[, default=nil])
|
||
|
||
--with-<withval>で指定されたオプション値を得る.
|
||
|
||
dir_config(target)
|
||
|
||
--with-<target>-dir, --with-<target>-include, --with-<target>-lib
|
||
のいずれかで指定されるディレクトリを $CFLAGS や $LDFLAGS
|
||
に追加する.
|
||
|
||
/*
|
||
* Local variables:
|
||
* fill-column: 60
|
||
* end:
|
||
*/
|