ruby/README.EXT.ja

1504 строки
42 KiB
Plaintext
Исходник Ответственный История

Этот файл содержит неоднозначные символы Юникода!

Этот файл содержит неоднозначные символы Юникода, которые могут быть перепутаны с другими в текущей локали. Если это намеренно, можете спокойно проигнорировать это предупреждение. Используйте кнопку Экранировать, чтобы подсветить эти символы.

.\" 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_NILT_FALSET_TRUEである時データはそれぞ
れnilfalsetrueですこのデータタイプのオブジェクトはひと
つずつしか存在しません.
データタイプが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* を取り出す場合, 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)
2Rubyの機能を使う
原理的に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 定数定義』で紹介さ
れている関数を使ってください.
3Rubyと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変数以降に指定したVALUEへの参照に値を代入してくれま
す.
引数をRubyの配列として受け取るメソッドの例には
Thread#initializeがあります実装はこうです
--
static VALUE
thread_initialize(VALUE thread, 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_exec.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
proc.c : Binding, Proc
process.c : Process
random.c : 乱数
range.c : Range
rational.c : Rational
re.c : Regexp, MatchData
signal.c : Signal
sprintf.c :
string.c : String
struct.c : Struct
time.c : Time
defs/knwon_errors.def : 例外クラス Errno::*
-> known_errors.inc : 自動生成
多言語化
encoding.c : Encoding
transcode.c : Encoding::Converter
enc/*.c : エンコーディングクラス群
enc/trans/* : コードポイント対応表
gorubyコマンドの実装
goruby.c
golf_prelude.rb : goruby固有のライブラリ
-> golf_prelude.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)
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形式で与えられた指定されたフォーマットに従って引
数を分解し続くVALUEへの参照にセットしますこのフォーマッ
トはABNFで記述すると以下の通りです
--
scan-arg-spec := param-arg-spec [block-arg-spec]
param-arg-spec := pre-arg-spec [post-arg-spec] / post-arg-spec / pre-opt-post-arg-spec
pre-arg-spec := num-of-leading-mandatory-args [num-of-optional-args]
post-arg-spec := sym-for-variable-length-args [num-of-trailing-mandatory-args]
pre-opt-post-arg-spec := num-of-leading-mandatory-args num-of-optional-args num-of-trailing-mandatory-args
block-arg-spec := sym-for-block-arg
num-of-leading-mandatory-args := DIGIT ; 先頭に置かれる省略不能な引数の数
num-of-optional-args := DIGIT ; 続いて置かれる省略可能な引数の数
sym-for-variable-length-args := "*" ; 続いて置かれる可変長引数を
; Rubyの配列で取得するための指定
num-of-trailing-mandatory-args := DIGIT ; 終端に置かれる省略不能な引数の数
sym-for-block-arg := "&" ; イテレータブロックを取得するための
; 指定
--
フォーマットが"12"の場合引数は最低1つで3つ(1+2)まで許さ
れるという意味になります.従って,フォーマット文字列に続い
て3つのVALUEへの参照を置く必要がありますそれらには取得した
変数がセットされます変数への参照の代わりにNULLを指定する
こともでき,その場合は取得した引数の値は捨てられます.なお、
省略可能引数が省略された時の変数の値はnil(C言語のレベルでは
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)
指定されたフック関数を削除します.
** 互換性のためのマクロ
APIの互換性をチェックするために以下のマクロがデフォルトで定義されています
NORETURN_STYLE_NEW
NORETURN マクロが関数型マクロとして定義されていることを意味する.
HAVE_RB_DEFINE_ALLOC_FUNC
rb_define_alloc_func() 関数が提供されていること、つまり
allocation framework が使われることを意味する.
have_func("rb_define_alloc_func", "ruby.h")
の結果と同じ.
HAVE_RB_REG_NEW_STR
StringオブジェクトからRegexpオブジェクトを作る
rb_reg_new_str() 関数が提供されていることを意味する.
have_func("rb_reg_new_str", "ruby.h").
の結果と同じ.
HAVE_RB_IO_T
rb_io_t 型が提供されていることを意味する.
USE_SYMBOL_AS_METHOD_NAME
メソッド名を返すメソッドModule#methods, #singleton_methods
などがSymbolを返すことを意味する
HAVE_RUBY_*_H
ruby.h で定義されている.対応するヘッダが提供されていること
を意味するたとえばHAVE_RUBY_ST_H が定義されている場合は
単なる st.h ではなく ruby/st.h を使用する.
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-<withval>で指定されたオプション値
を得る.
enable_config(config, *defaults)
disable_config(config, *defaults)
コマンドライン上の--enable-<config>または
--disable-<config>で指定された真偽値を得る.
--enable-<config>が指定されていた場合はtrue
--disable-<config>が指定されていた場合はfalseを返す
どちらも指定されていない場合は,ブロックつきで呼び出されて
いる場合は*defaultsをyieldした結果ブロックなしなら
*defaultsを返す
dir_config(target[, default_dir])
dir_config(target[, default_include, default_lib])
コマンドライン上の--with-<target>-dir, --with-<target>-include,
--with-<target>-libのいずれかで指定されるディレクトリを
$CFLAGS や $LDFLAGS に追加する.--with-<target>-dir=/pathは
--with-<target>-include=/path/include --with-<target>-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:
*/