зеркало из https://github.com/github/ruby.git
1065 строки
28 KiB
Plaintext
1065 строки
28 KiB
Plaintext
.\" README.EXT - -*- Text -*- created at: Mon Aug 7 16:45:54 JST 1995
|
||
|
||
This document explains how to make extention modules for ruby.
|
||
|
||
1.Basic knowledge
|
||
|
||
In C, variables have types and data do not have types. In contrast,
|
||
ruby variables do not have static type and data themselves have
|
||
types. So, data need to be converted across the languages.
|
||
|
||
Data in ruby represented C type `VALUE'. Each VALUE data have its
|
||
data-type.
|
||
|
||
rubyのデータはVALUEというCの型で表現されます.VALUE型のデー
|
||
タはそのデータタイプを自分で知っています.このデータタイプと
|
||
いうのはデータ(オブジェクト)の実際の構造を意味していて,ruby
|
||
のクラスとはまた違ったものです.
|
||
|
||
To retrieve an C data from the VALUE, you need to:
|
||
|
||
(1) Identify VALUE's data type
|
||
(2) Convert VALUE into C data
|
||
|
||
Converting to wrong data type may cause serious promblems.
|
||
|
||
|
||
1.1 Data-types
|
||
|
||
Ruby interpreter has data-types as below:
|
||
|
||
T_NIL nil
|
||
T_OBJECT ordinaly object
|
||
T_CLASS class
|
||
T_MODULE module
|
||
T_FLOAT floating point number
|
||
T_STRING string
|
||
T_REGEXP regular expression
|
||
T_ARRAY array
|
||
T_FIXNUM Fixnum(31bit integer)
|
||
T_HASH assosiative array
|
||
T_STRUCT (ruby) structure
|
||
T_BIGNUM multi precision integer
|
||
T_TRUE true
|
||
T_FALSE false
|
||
T_DATA data
|
||
|
||
Otherwise, there are several other types used internally:
|
||
|
||
T_ICLASS
|
||
T_MATCH
|
||
T_VARMAP
|
||
T_SCOPE
|
||
T_NODE
|
||
|
||
Most of the types are represented by C structures.
|
||
|
||
1.2 Check Data Type of the VALUE
|
||
|
||
The macro TYPE() defined in ruby.h shows data-type of the VALUE.
|
||
TYPE() returns the constant number T_XXXX described above. To handle
|
||
data-types, the code will be like:
|
||
|
||
switch (TYPE(obj)) {
|
||
case T_FIXNUM:
|
||
/* process Fixnum */
|
||
break;
|
||
case T_STRING:
|
||
/* process String */
|
||
break;
|
||
case T_ARRAY:
|
||
/* process Array */
|
||
break;
|
||
default:
|
||
/* raise exception */
|
||
Fail("not valid value");
|
||
break;
|
||
}
|
||
|
||
There is the data-type check function.
|
||
|
||
void Check_Type(VALUE value, int type)
|
||
|
||
It raises an exception, if the VALUE does not have the type specified.
|
||
|
||
There are faster check-macros for fixnums and nil.
|
||
|
||
FIXNUM_P(obj)
|
||
NIL_P(obj)
|
||
|
||
1.3 Convert VALUE into C data
|
||
|
||
データタイプが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 Convert C data into 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 Manipulate ruby data
|
||
|
||
先程も述べた通り,rubyの構造体をアクセスする時に内容の更新を
|
||
行うことは勧められません.で,rubyのデータを操作する時には
|
||
rubyが用意している関数を用いてください.
|
||
|
||
ここではもっとも使われるであろう文字列と配列の生成/操作を行
|
||
い関数をあげます(全部ではないです).
|
||
|
||
String funtions
|
||
|
||
str_new(char *ptr, int len)
|
||
|
||
Creates a new ruby string.
|
||
|
||
str_new2(char *ptr)
|
||
|
||
Creates a new ruby string from C string. This is equivalent to
|
||
str_new(ptr, strlen(ptr)).
|
||
|
||
str_cat(VALUE str, char *ptr, int len)
|
||
|
||
Appends len bytes data from ptr to the ruby string.
|
||
|
||
Array functions
|
||
|
||
ary_new()
|
||
|
||
Creates an array with no element.
|
||
|
||
ary_new2(int len)
|
||
|
||
Creates an array with no element, with allocating internal buffer
|
||
for len elements.
|
||
|
||
ary_new3(int n, ...)
|
||
|
||
Creates an n-elements array from arguments.
|
||
|
||
ary_new4(int n, VALUE *elts)
|
||
|
||
Creates an n-elements array from C array.
|
||
|
||
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 operations. The first argument to each functions must be an
|
||
array. They may dump core if other types given.
|
||
|
||
2. Extend ruby with C
|
||
|
||
原理的にrubyで書けることはCでも書けます.rubyそのものがCで記
|
||
述されているんですから,当然といえば当然なんですけど.ここで
|
||
はrubyの拡張に使うことが多いだろうと予測される機能を中心に紹
|
||
介します.
|
||
|
||
2.1 Add new features to ruby
|
||
|
||
rubyで提供されている関数を使えばrubyインタプリタに新しい機能
|
||
を追加することができます.rubyでは以下の機能を追加する関数が
|
||
提供されています.
|
||
|
||
* クラス,モジュール
|
||
* メソッド,特異メソッドなど
|
||
* 定数
|
||
|
||
では順に紹介します.
|
||
|
||
2.1.1 Class/module definition
|
||
|
||
クラスやモジュールを定義するためには,以下の関数を使います.
|
||
|
||
VALUE rb_define_class(char *name, VALUE super)
|
||
VALUE rb_define_module(char *name)
|
||
|
||
これらの関数は新しく定義されたクラスやモジュールを返します.
|
||
メソッドや定数の定義にこれらの値が必要なので,ほとんどの場合
|
||
は戻り値を変数に格納しておく必要があるでしょう.
|
||
|
||
2.1.2 Method/singleton method definition
|
||
|
||
メソッドや特異メソッドを定義するには以下の関数を使います.
|
||
|
||
void rb_define_method(VALUE class, char *name,
|
||
VALUE (*func)(), int argc)
|
||
|
||
void rb_define_singleton_method(VALUE object, char *name,
|
||
VALUE (*func)(), int argc)
|
||
|
||
|
||
念のため説明すると「特異メソッド」とは,その特定のオブジェク
|
||
トに対してだけ有効なメソッドです.rubyではよくSmalltalkにお
|
||
けるクラスメソッドとして,クラスに対する特異メソッドが使われ
|
||
ます.
|
||
|
||
これらの関数の argcという引数はCの関数へ渡される引数の数(と
|
||
形式)を決めます.argcが正の時は関数に引き渡す引数の数を意味
|
||
します.16個以上の引数は使えません(が,要りませんよね,そん
|
||
なに).
|
||
|
||
argcが負の時は引数の数ではなく,形式を指定したことになります.
|
||
argcが-1の時は引数を配列に入れて渡されます.argcが-2の時は引
|
||
数はrubyの配列として渡されます.
|
||
|
||
メソッドを定義する関数はもう二つあります.ひとつはprivateメ
|
||
ソッドを定義する関数で,引数はrb_define_method()と同じです.
|
||
|
||
void rb_define_private_method(VALUE class, char *name,
|
||
VALUE (*func)(), int argc)
|
||
|
||
privateメソッドとは関数形式でしか呼び出すことの出来ないメソッ
|
||
ドです.
|
||
|
||
もうひとつはモジュール関数を定義するものです.モジュール関数
|
||
とはモジュールの特異メソッドであり,同時にprivateメソッドで
|
||
もあるものです.例をあげるとMathモジュールのsqrt()などがあげ
|
||
られます.このメソッドは
|
||
|
||
Math.sqrt(4)
|
||
|
||
という形式でも
|
||
|
||
include Math
|
||
sqrt(4)
|
||
|
||
という形式でも使えます.モジュール関数を定義する関数は以下の
|
||
通りです.
|
||
|
||
void rb_define_module_function(VALUE module, char *name,
|
||
VALUE (*func)(), int argc)
|
||
|
||
関数的メソッド(Kernelモジュールのprivaet method)を定義するた
|
||
めの関数は以下の通りです.
|
||
|
||
void rb_define_global_function(char *name, VALUE (*func)(), int argc)
|
||
|
||
|
||
2.1.3 Constant definition
|
||
|
||
拡張モジュールが必要な定数はあらかじめ定義しておいた方が良い
|
||
でしょう.定数を定義する関数は二つあります.
|
||
|
||
void rb_define_const(VALUE class, char *name, VALUE val)
|
||
void rb_define_global_const(char *name, VALUE val)
|
||
|
||
前者は特定のクラス/モジュールに属する定数を定義するもの,後
|
||
者はグローバルな定数を定義するものです.
|
||
|
||
2.2 Use ruby features from C
|
||
|
||
既に『1.5 rubyのデータを操作する』で一部紹介したような関数を
|
||
使えば,rubyの機能を実現している関数を直接呼び出すことが出来
|
||
ます.
|
||
|
||
# このような関数の一覧表はいまのところありません.ソースを見
|
||
# るしかないですね.
|
||
|
||
それ以外にもrubyの機能を呼び出す方法はいくつかあります.
|
||
|
||
2.2.1 rubyのプログラムをevalする
|
||
|
||
Cからrubyの機能を呼び出すもっとも簡単な方法として,文字列で
|
||
与えられたrubyのプログラムを評価する関数があります.
|
||
|
||
VALUE rb_eval_string(char *str)
|
||
|
||
この評価は現在の環境で行われます.つまり,現在のローカル変数
|
||
などを受け継ぎます.
|
||
|
||
2.2.2 ID or Symbol
|
||
|
||
Cから文字列を経由せずにrubyのメソッドを呼び出すこともできま
|
||
す.その前に,rubyインタプリタ内でメソッドや変数名を指定する
|
||
時に使われているIDについて説明しておきましょう.
|
||
|
||
IDとは変数名,メソッド名を表す整数です.rubyの中では
|
||
|
||
:識別子
|
||
|
||
でアクセスできます.Cからこの整数を得るためには関数
|
||
|
||
rb_intern(char *name)
|
||
|
||
を使います.また一文字の演算子はその文字コードがそのままシン
|
||
ボルになっています.
|
||
|
||
2.2.3 Invoke ruby method from C
|
||
|
||
Cから文字列を経由せずにrubyのメソッドを呼び出すためには以下
|
||
の関数を使います.
|
||
|
||
VALUE rb_funcall(VALUE recv, ID mid, int argc, ...)
|
||
|
||
この関数はオブジェクトrecvのmidで指定されるメソッドを呼び出
|
||
します.
|
||
|
||
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. Informatin sharing between ruby and C
|
||
|
||
C言語とrubyの間で情報を共有する方法について解説します.
|
||
|
||
3.1 Ruby constant that Cから参照できるrubyの定数
|
||
|
||
Following ruby constants can be referred from C.
|
||
|
||
TRUE
|
||
FALSE
|
||
|
||
Boolean values. FALSE is false in the C also (i.e. 0).
|
||
|
||
Qnil
|
||
|
||
Ruby nil in C scope.
|
||
|
||
3.2 Global variables shared between C and ruby
|
||
|
||
Cとrubyで大域変数を使って情報を共有できます.共有できる大域
|
||
変数にはいくつかの種類があります.そのなかでもっとも良く使わ
|
||
れると思われるのはrb_define_variable()です.
|
||
|
||
void rb_define_variable(char *name, VALUE *var)
|
||
|
||
この関数はrubyとCとで共有する大域変数を定義します.変数名が
|
||
`$'で始まらない時には自動的に追加されます.この変数の値を変
|
||
更すると自動的にrubyの対応する変数の値も変わります.
|
||
|
||
またruby側からは更新できない変数もあります.このread onlyの
|
||
変数は以下の関数で定義します.
|
||
|
||
void rb_define_readonly_variable(char *name, VALUE *var)
|
||
|
||
これら変数の他にhookをつけた大域変数を定義できます.hook付き
|
||
の大域変数は以下の関数を用いて定義します.hook付き大域変数の
|
||
値の参照や設定はhookで行う必要があります.
|
||
|
||
void rb_define_hooked_variable(char *name, VALUE *var,
|
||
VALUE (*getter)(), VALUE (*setter)())
|
||
|
||
この関数はCの関数によってhookのつけられた大域変数を定義しま
|
||
す.変数が参照された時には関数getterが,変数に値がセットされ
|
||
た時には関数setterが呼ばれる.hookを指定しない場合はgetterや
|
||
setterに0を指定します.
|
||
|
||
# getterもsetterも0ならばrb_define_variable()と同じになる.
|
||
|
||
それから,Cの関数によって実現されるrubyの大域変数を定義する
|
||
関数があります.
|
||
|
||
void rb_define_virtual_variable(char *name,
|
||
VALUE (*getter)(), VALUE (*setter)())
|
||
|
||
この関数によって定義されたrubyの大域変数が参照された時には
|
||
getterが,変数に値がセットされた時にはsetterが呼ばれます.
|
||
|
||
The prototypes of the getter and setter functions are as following:
|
||
|
||
(*getter)(ID id, void *data, struct global_entry* entry);
|
||
(*setter)(VALUE val, ID id, void *data, struct global_entry* entry);
|
||
|
||
3.3 Encapsulate C data into ruby object
|
||
|
||
Cの世界で定義されたデータ(構造体)をrubyのオブジェクトとして
|
||
取り扱いたい場合がありえます.このような場合には,Dataという
|
||
rubyオブジェクトにCの構造体(へのポインタ)をくるむことでruby
|
||
オブジェクトとして取り扱えるようになります.
|
||
|
||
Dataオブジェクトを生成して構造体をrubyオブジェクトにカプセル
|
||
化するためには,以下のマクロを使います.
|
||
|
||
Data_Wrap_Struct(class,mark,free,ptr)
|
||
|
||
このマクロの戻り値は生成されたDataオブジェクトです.
|
||
|
||
classはこのDataオブジェクトのクラスです.ptrはカプセル化する
|
||
Cの構造体へのポインタです.markはこの構造体がrubyのオブジェ
|
||
クトへの参照がある時に使う関数です.そのような参照を含まない
|
||
時には0を指定します.
|
||
|
||
# そのような参照は勧められません.
|
||
|
||
freeはこの構造体がもう不要になった時に呼ばれる関数です.この
|
||
関数がガーベージコレクタから呼ばれます.
|
||
|
||
Cの構造体の割当とDataオブジェクトの生成を同時に行うマクロと
|
||
して以下のものが提供されています.
|
||
|
||
Data_Make_Struct(class, type, mark, free, sval)
|
||
|
||
このマクロの戻り値は生成されたDataオブジェクトです.
|
||
|
||
class, mark, freeはData_Wrap_Structと同じ働きをします.type
|
||
は割り当てるC構造体の型です.割り当てられた構造体は変数sval
|
||
に代入されます.この変数の型は (type*) である必要があります.
|
||
|
||
Dataオブジェクトからポインタを取り出すのは以下のマクロを用い
|
||
ます.
|
||
|
||
Data_Get_Struct(obj, type, sval)
|
||
|
||
Cの構造体へのポインタは変数svalに代入されます.
|
||
|
||
これらのDataの使い方はちょっと分かりにくいので,後で説明する
|
||
例題を参照してください.
|
||
|
||
4.Example - Create dbm module
|
||
|
||
ここまでの説明でとりあえず拡張モジュールは作れるはずです.
|
||
rubyのextディレクトリにすでに含まれているdbmモジュールを例に
|
||
して段階的に説明します.
|
||
|
||
(1) make the directory
|
||
|
||
% mkdir ext/dbm
|
||
|
||
rubyを展開したディレクトリの下,extディレクトリの中に拡張モ
|
||
ジュール用のディレクトリを作ります.名前は適当に選んで構いま
|
||
せん.
|
||
|
||
(2) create MANIFEST file
|
||
|
||
% cd ext/dbm
|
||
% touch MANIFEST
|
||
|
||
拡張モジュールのディレクトリの下にはMANIFESTというファイルが
|
||
必要なので,とりあえず空のファイルを作っておきます.後でこの
|
||
ファイルには必要なファイル一覧が入ることになります.
|
||
|
||
MANIFESTというファイルは,makeの時にディレクトリが拡張モジュー
|
||
ルを含んでいるかどうか判定するために使われれています.
|
||
|
||
(3) design the library
|
||
|
||
まあ,当然なんですけど,どういう機能を実現するかどうかまず設
|
||
計する必要があります.どんなクラスをつくるか,そのクラスには
|
||
どんなメソッドがあるか,クラスが提供する定数などについて設計
|
||
します.dbmクラスについてはext/dbm.docを参照してください.
|
||
|
||
(4) write C code.
|
||
|
||
拡張モジュール本体となるC言語のソースを書きます.C言語のソー
|
||
スがひとつの時には「モジュール名.c」を選ぶと良いでしょう.C
|
||
言語のソースが複数の場合には逆に「モジュール名.c」というファ
|
||
イル名は避ける必要があります.オブジェクトファイルとモジュー
|
||
ル生成時に中間的に生成される「モジュール名.o」というファイル
|
||
とが衝突するからです.
|
||
|
||
rubyは拡張モジュールをロードする時に「Init_モジュール名」と
|
||
いう関数を自動的に実行します.dbmモジュールの場合「Init_dbm」
|
||
です.この関数の中でクラス,モジュール,メソッド,定数などの
|
||
定義を行います.dbm.cから一部引用します.
|
||
|
||
--
|
||
Init_dbm()
|
||
{
|
||
/* DBMクラスを定義する */
|
||
cDBM = rb_define_class("DBM", cObject);
|
||
/* DBMはEnumerateモジュールをインクルードする */
|
||
rb_include_module(cDBM, 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(class,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, class)
|
||
int argc;
|
||
VALUE *argv;
|
||
VALUE class;
|
||
{
|
||
:
|
||
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;
|
||
struct RArray *args;
|
||
{
|
||
:
|
||
}
|
||
--
|
||
|
||
第1引数はself,第2引数はrubyの配列です.ここではキャストを減
|
||
らすため struct RArray* で受けていますが,VALUEでも同じこと
|
||
です.
|
||
|
||
** 注意事項
|
||
|
||
rubyと共有はしないがrubyのオブジェクトを格納する可能性のある
|
||
Cの大域変数は以下の関数を使ってrubyインタプリタに変数の存在
|
||
を教えてあげてください.でないとGCでトラブルを起こします.
|
||
|
||
void rb_global_variable(VALUE *var)
|
||
|
||
(5) prepare extconf.rb
|
||
|
||
もしディレクトリに「extconf.rb」というファイルが存在すれば,
|
||
make時に実行されます.なければ適当にMakefileが生成されます.
|
||
|
||
extconf.rbはモジュールのコンパイルに必要な条件のチェックなど
|
||
を行うことが目的です.extconf.rbの中では以下のruby関数を使う
|
||
ことが出来ます.
|
||
|
||
have_library(lib, func): ライブラリの存在チェック
|
||
have_func(func): 関数の存在チェック
|
||
have_header(header): ヘッダファイルの存在チェック
|
||
create_makefile(target): Makefileの生成
|
||
|
||
以下の変数を使うことができます.
|
||
|
||
$CFLAGS: コンパイル時に追加的に指定するフラグ(-Iなど)
|
||
$LDFLAGS: リンク時に追加的に指定するフラグ(-Lなど)
|
||
|
||
モジュールをコンパイルする条件が揃わなず,そのモジュールはコ
|
||
ンパイルしない時にはcreate_makefileを呼ばなければMakefileは
|
||
生成されず,コンパイルも行われません.
|
||
|
||
(6) prepare depend (optional)
|
||
|
||
もし,ディレクトリにdependというファイルが存在すれば,
|
||
Makefileが依存関係をチェックしてくれます.
|
||
|
||
% gcc -MM *.c > depend
|
||
|
||
などで作ることが出来ます.あって損は無いでしょう.
|
||
|
||
(7) MANIFESTファイルにファイル名を入れる
|
||
|
||
% ls > MANIFEST
|
||
% vi MANIFEST
|
||
|
||
*.o, *~など不必要なファイル以外はMANIFESTに追加しておきます.
|
||
make時にはMANIFESTの内容は参照しませんので,空のままでも問題
|
||
は起きませんが,パッケージングの時に参照することがあるのと,
|
||
必要なファイルを区別できるので,用意しておいた方が良いでしょ
|
||
う.
|
||
|
||
(8) make
|
||
|
||
rubyのディレクトリでmakeを実行するとMakefileを生成からmake,
|
||
必要によってはそのモジュールのrubyへのリンクまで自動的に実行
|
||
してくれます.extconf.rbを書き換えるなどしてMakefileの再生成
|
||
が必要な時はまたrubyディレクトリでmakeしてください.
|
||
|
||
(9) debug
|
||
|
||
まあ,デバッグしないと動かないでしょうね.ext/Setupにディレ
|
||
クトリ名を書くと静的にリンクするのでデバッガが使えるようにな
|
||
ります.その分コンパイルが遅くなりますけど.
|
||
|
||
(10) done, now you have the extension module
|
||
|
||
後はこっそり使うなり,広く公開するなり,売るなり,ご自由にお
|
||
使いください.rubyの作者は拡張モジュールに関して一切の権利を
|
||
主張しません.
|
||
|
||
Appendix A. rubyのソースコードの分類
|
||
|
||
rubyのソースはいくつかに分類することが出来ます.このうちクラ
|
||
スライブラリの部分は基本的に拡張モジュールと同じ作り方になっ
|
||
ています.これらのソースは今までの説明でほとんど理解できると
|
||
思います.
|
||
|
||
coore ruby language
|
||
|
||
class.c
|
||
error.c
|
||
eval.c
|
||
gc.c
|
||
object.c
|
||
parse.y
|
||
variable.c
|
||
|
||
utility functions
|
||
|
||
dln.c
|
||
fnmatch.c
|
||
glob.c
|
||
regex.c
|
||
st.c
|
||
util.c
|
||
|
||
ruby interpreter implementation
|
||
|
||
dmyext.c
|
||
inits.c
|
||
main.c
|
||
ruby.c
|
||
version.c
|
||
|
||
class library
|
||
|
||
array.c
|
||
bignum.c
|
||
compar.c
|
||
dir.c
|
||
enum.c
|
||
file.c
|
||
hash.c
|
||
io.c
|
||
math.c
|
||
numeric.c
|
||
pack.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で始まる構造体
|
||
名を全て大文字にした名前のマクロが用意されている.
|
||
|
||
** Variables and constants
|
||
|
||
Qnil
|
||
|
||
const: nil object
|
||
|
||
TRUE
|
||
|
||
const: TRUE object(default true value)
|
||
|
||
FALSE
|
||
|
||
const: FALSE object
|
||
|
||
** Cデータのカプセル化
|
||
|
||
Data_Wrap_Struct(VALUE class, void (*mark)(), void (*free)(), void *sval)
|
||
|
||
Cの任意のポインタをカプセル化したrubyオブジェクトを返す.こ
|
||
のポインタがrubyからアクセスされなくなった時,freeで指定した
|
||
関数が呼ばれる.また,このポインタの指すデータが他のrubyオブ
|
||
ジェクトを指している場合,markに指定する関数でマークする必要
|
||
がある.
|
||
|
||
Data_Make_Struct(class, type, mark, free, sval)
|
||
|
||
type型のメモリをmallocし,変数svalに代入した後,それをカプセ
|
||
ル化したデータを返すマクロ.
|
||
|
||
Data_Get_Struct(data, type, sval)
|
||
|
||
dataからtype型のポインタを取り出し変数svalに代入するマクロ.
|
||
|
||
** クラス/モジュール定義
|
||
|
||
VALUE rb_define_class(char *name, VALUE super)
|
||
|
||
superのサブクラスとして新しいrubyクラスを定義する.
|
||
|
||
VALUE rb_define_class_under(VALUE module, char *name, VALUE super)
|
||
|
||
superのサブクラスとして新しいrubyクラスを定義し,moduleの定
|
||
数として定義する.
|
||
|
||
VALUE rb_define_module(char *name)
|
||
|
||
新しいrubyモジュールを定義する.
|
||
|
||
VALUE rb_define_module_under(VALUE module, char *name, VALUE super)
|
||
|
||
新しいrubyモジュールを定義し,moduleの定数として定義する.
|
||
|
||
void rb_include_module(VALUE class, VALUE module)
|
||
|
||
モジュールをインクルードする.classがすでにmoduleをインクルー
|
||
ドしている時には何もしない(多重インクルードの禁止).
|
||
|
||
void rb_extend_object(VALUE object, VALUE module)
|
||
|
||
オブジェクトをモジュール(で定義されているメソッド)で拡張する.
|
||
|
||
** 大域変数定義
|
||
|
||
void rb_define_variable(char *name, VALUE *var)
|
||
|
||
rubyとCとで共有するグローバル変数を定義する.変数名が`$'で始
|
||
まらない時には自動的に追加される.nameとしてrubyの識別子とし
|
||
て許されない文字(例えば` ')を含む場合にはrubyプログラムから
|
||
は見えなくなる.
|
||
|
||
void rb_define_readonly_variable(char *name, VALUE *var)
|
||
|
||
rubyとCとで共有するread onlyのグローバル変数を定義する.read
|
||
onlyであること以外はrb_define_variable()と同じ.
|
||
|
||
void rb_define_virtual_variable(char *name,
|
||
VALUE (*getter)(), VALUE (*setter)())
|
||
|
||
関数によって実現されるruby変数を定義する.変数が参照された時
|
||
にはgetterが,変数に値がセットされた時にはsetterが呼ばれる.
|
||
|
||
void rb_define_hooked_variable(char *name, VALUE *var,
|
||
VALUE (*getter)(), VALUE (*setter)())
|
||
|
||
関数によってhookのつけられたグローバル変数を定義する.変数が
|
||
参照された時にはgetterが,関数に値がセットされた時にはsetter
|
||
が呼ばれる.getterやsetterに0を指定した時にはhookを指定しな
|
||
いのと同じ事になる.
|
||
|
||
void rb_global_variable(VALUE *var)
|
||
|
||
GCのため,rubyプログラムからはアクセスされないが, rubyオブジェ
|
||
クトを含む大域変数をマークする.
|
||
|
||
** クラス定数
|
||
|
||
void rb_define_const(VALUE class, char *name, VALUE val)
|
||
|
||
クラス定数を定義する.
|
||
|
||
void rb_define_global_const(char *name, VALUE val)
|
||
|
||
大域定数を定義する.
|
||
|
||
rb_define_const(cKernal, name, val)
|
||
|
||
と同じ意味.
|
||
|
||
** メソッド定義
|
||
|
||
rb_define_method(VALUE class, 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 class, char *name, VALUE (*func)(), int argc)
|
||
|
||
privateメソッドを定義する.引数はrb_define_method()と同じ.
|
||
|
||
rb_define_singleton_method(VALUE class, char *name, VALUE (*func)(), int argc)
|
||
|
||
特異メソッドを定義する.引数はrb_define_method()と同じ.
|
||
|
||
rb_scan_args(int atgc, VALUE *argv, 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(char *str)
|
||
|
||
文字列をrubyとスクリプトしてコンパイル・実行する.
|
||
|
||
ID rb_intern(char *name)
|
||
|
||
文字列に対応するIDを返す.
|
||
|
||
char *rb_id2name(ID id)
|
||
|
||
IDに対応する文字列を返す(デバッグ用).
|
||
|
||
char *rb_class2name(VALUE class)
|
||
|
||
classの名前を返す(デバッグ用).classが名前を持たない時には,
|
||
祖先を遡って名前を持つクラスの名前を返す.
|
||
|
||
** インスタンス変数
|
||
|
||
VALUE rb_iv_get(VALUE obj, char *name)
|
||
|
||
objのインスタンス変数の値を得る.`@'で始まらないインスタンス
|
||
変数は rubyプログラムからアクセスできない「隠れた」インスタ
|
||
ンス変数になる.
|
||
|
||
VALUE rb_iv_set(VALUE obj, char *name, VALUE val)
|
||
|
||
objのインスタンス変数をvalにセットする.
|
||
|
||
** 制御構造
|
||
|
||
VALUE rb_iterate(VALUE (*func1)(), void *arg1, VALUE (*func2)(), void *arg2)
|
||
|
||
func2をブロックとして設定し, func1をイテレータとして呼ぶ.
|
||
func1には arg1が引数として渡され, func2には第1引数にイテレー
|
||
タから与えられた値, 第2引数にarg2が渡される.
|
||
|
||
VALUE rb_yield(VALUE val)
|
||
|
||
valを値としてイテレータブロックを呼び出す.
|
||
|
||
VALUE rb_rescue(VALUE (*func1)(), void *arg1, VALUE (*func2)(), void *arg2)
|
||
|
||
関数func1をarg1を引数に呼び出す.func1の実行中に例外が発生し
|
||
た時には func2をarg2を引数として呼ぶ.戻り値は例外が発生しな
|
||
かった時はfunc1の戻り値, 例外が発生した時にはfunc2の戻り値で
|
||
ある.
|
||
|
||
VALUE rb_ensure(VALUE (*func1)(), void *arg1, void (*func2)(), void *arg2)
|
||
|
||
関数func1をarg1を引数として実行し, 実行終了後(たとえ例外が発
|
||
生しても) func2をarg2を引数として実行する.戻り値はfunc1の戻
|
||
り値である(例外が発生した時は戻らない).
|
||
|
||
** 例外・エラー
|
||
|
||
void Warning(char *fmt, ...)
|
||
|
||
verbose時に標準エラー出力に警告情報を表示する.引数はprintf()と同じ.
|
||
|
||
void Fail(char *fmt, ...)
|
||
|
||
例外を発生させる.引数はprintf()と同じ.
|
||
|
||
void Fatal(char *fmt, ...)
|
||
|
||
致命的例外を発生させる.通常の例外処理は行なわれず, インター
|
||
プリタが終了する(ただしensureで指定されたコードは終了前に実
|
||
行される).
|
||
|
||
void Bug(char *fmt, ...)
|
||
|
||
インタープリタなどプログラムのバグでしか発生するはずのない状
|
||
況の時呼ぶ.インタープリタはコアダンプし直ちに終了する.例外
|
||
処理は一切行なわれない.
|
||
|
||
** rubyの初期化・実行
|
||
|
||
rubyをアプリケーションに埋め込む場合には以下のインタフェース
|
||
を使う.通常の拡張モジュールには必要ない.
|
||
|
||
void ruby_init(int argc, char **argv, char **envp)
|
||
|
||
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を返す.
|
||
|
||
have_func(func)
|
||
|
||
関数funcの存在をチェックする.funcが標準ではリンクされないラ
|
||
イブラリ内のものである時には先にhave_libraryでそのライブラリ
|
||
をチェックしておく事.関数が存在する時TRUEを返す.
|
||
|
||
have_header(header)
|
||
|
||
ヘッダファイルの存在をチェックする.ヘッダファイルが存在する
|
||
時TRUEを返す.
|
||
|
||
create_makefile(target)
|
||
|
||
拡張モジュール用のMakefileを生成する.この関数を呼ばなければ
|
||
そのモジュールはコンパイルされない.targetはモジュール名を表
|
||
す.
|
||
|
||
/*
|
||
* Local variables:
|
||
* fill-column: 60
|
||
* end:
|
||
*/
|