Rubyの拡張ライブラリの作り方
Rubyの拡張ライブラリの作り方を説明します.
基礎知識
Cの変数には型があり,データには型がありません.ですから,たとえばポインタをintの変数に代入すると,その値は整数として取り扱われます.逆にRubyの変数には型がなく,データに型があります.この違いのため,CとRubyは相互に変換しなければ,お互いのデータをアクセスできません.
RubyのデータはVALUEというCの型で表現されます.VALUE型のデータはそのデータタイプを自分で知っています.このデータタイプというのはデータ(オブジェクト)の実際の構造を意味していて,Ruby のクラスとはまた違ったものです.
VALUEからCにとって意味のあるデータを取り出すためには
-
VALUEのデータタイプを知る
-
VALUEをCのデータに変換する
の両方が必要です.(1)を忘れると間違ったデータの変換が行われて,最悪プログラムがcore dumpします.
データタイプ
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-
includeされたモジュール
T_MATCH-
MatchDataオブジェクト
T_UNDEF-
未定義値
T_NODE-
シンタックスツリーのノード
T_ZOMBIE-
解放待ち中のオブジェクト
ほとんどのタイプはCの構造体で実装されています.
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)
VALUEをCのデータに変換する
データタイプがT_NIL,T_FALSE,T_TRUEである時,データはそれぞれnil,false,trueです.このデータタイプのオブジェクトはひとつずつしか存在しません.
データタイプが 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()).ただし,構造体への直接のアクセスはできるだけ避け,対応するrb_xxxx() といった関数を使うようにして下さい.例えば,配列の要素へアクセスする場合は,rb_ary_entry(ary, offset),rb_ary_store(ary, offset, obj) を利用するようにして下さい.
構造体からデータを取り出すマクロが提供されています.文字列 str の長さを得るためには「RSTRING_LEN(str)」とし,文字列 str をchar*として得るためには「RSTRING_PTR(str)」とします.
Rubyの構造体を直接アクセスする時に気をつけなければならないことは,配列や文字列の構造体の中身は参照するだけで,直接変更しないことです.直接変更した場合,オブジェクトの内容の整合性がとれなくなって,思わぬバグの原因になります.
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に変換してくれます(が,少し遅い).
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_str_new_literal(const char *ptr)-
Cのリテラル文字列からRubyの文字列を生成する.
rb_str_append(VALUE str1, VALUE str2)-
Rubyの文字列 str1 にRubyの文字列 str2 を追加する.
rb_sprintf(const char *format, ...)rb_vsprintf(const char *format, va_list ap)-
Cの文字列 format と続く引数をprintf(3)のフォーマットにしたがって整形し,Rubyの文字列を生成する.
注意:
"%"PRIsVALUEがObject#to_s(‘+’フラグが指定されているときはObject#inspect)を使ったVALUEの出力に利用できる.これは"%i"と衝突するため,整数には"%d"を使用すること. rb_str_cat(VALUE str, const char *ptr, long len)-
Rubyの文字列 str に len バイトの文字列 ptr を追加する.
rb_str_cat2(VALUE str, const char* ptr)rb_str_cat_cstr(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_append(str, rb_sprintf(format, ...))やrb_str_append(str, rb_vsprintf(format, ap))と同等である. rb_enc_str_new(const char *ptr, long len, rb_encoding *enc)rb_enc_str_new_cstr(const char *ptr, rb_encoding *enc)-
指定されたエンコーディングでRubyの文字列を生成する.
rb_enc_str_new_literal(const char *ptr, rb_encoding *enc)-
Cのリテラル文字列から指定されたエンコーディングでRubyの文字列を生成する.
rb_usascii_str_new(const char *ptr, long len)rb_usascii_str_new_cstr(const char *ptr)-
エンコーディングがUS-ASCIIのRubyの文字列を生成する.
rb_usascii_str_new_literal(const char *ptr)-
Cのリテラル文字列からエンコーディングがUS-ASCIIのRubyの文字列を生成する.
rb_utf8_str_new(const char *ptr, long len)rb_utf8_str_new_cstr(const char *ptr)-
エンコーディングがUTF-8のRubyの文字列を生成する.
rb_utf8_str_new_literal(const char *ptr)-
Cのリテラル文字列からエンコーディングがUTF-8のRubyの文字列を生成する.
rb_str_resize(VALUE str, long len)-
Rubyの文字列のサイズを len バイトに変更する.str の長さは前以てセットされていなければならない.len が元の長さよりも短い時は,len バイトを越えた部分の内容は捨てられる.len が元の長さよりも長い時は,元の長さを越えた部分の内容は保存されないでゴミになるだろう.この関数の呼び出しによって
RSTRING_PTR(str)が変更されるかもしれないことに注意. rb_str_set_len(VALUE str, long len)-
Rubyの文字列のサイズを len バイトにセットする.str が変更可能でなければ例外が発生する.
RSTRING_LEN(str)とは無関係に,len_ バイトまでの内容は保存される.len は str の容量を越えていてはならない. rb_str_modify(VALUE str)-
Rubyの文字列の変更する準備をする.str が変更可能でなければ例外が発生する.str のバッファが共有されている場合は,新しいバッファを割り当てて共有されていない状態にする.
RSTRING_PTRを使って中身を変更したり,rb_str_set_lenを呼んだりする前には,必ずこの関数を呼ばなけれならない.
配列に対する関数
rb_ary_new()-
要素が0の配列を生成する.
rb_ary_new2(long len)rb_ary_new_capa(long len)-
要素が0の配列を生成する.len 要素分の領域をあらかじめ割り当てておく.
rb_ary_new3(long n, ...)rb_ary_new_from_args(long n, ...)-
引数で指定した n 要素を含む配列を生成する.
rb_ary_new4(long n, VALUE *elts)rb_ary_new_from_values(long n, VALUE *elts)-
配列で与えた n 要素の配列を生成する.
rb_ary_to_ary(VALUE obj)-
オブジェクトを配列に変換する. Object#to_aryと同等である.
他にも配列を操作する関数が多数ある. これらは引数aryに配列を渡さなければならない. さもないとコアを吐く.
rb_ary_aref(int argc, const VALUE *argv, VALUE ary)-
Array#[]と同等. rb_ary_entry(VALUE ary, long offset)-
ary[offset] rb_ary_store(VALUE ary, long offset, VALUE obj)-
ary[offset] = obj 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)-
ary.push(val),ary.pop,ary.shift,ary.unshift(val) rb_ary_cat(VALUE ary, const VALUE *ptr, long len)-
配列 ary に ptr から len 個のオブジェクトを追加する.
Rubyの機能を使う
原理的にRubyで書けることはCでも書けます.RubyそのものがCで記述されているんですから,当然といえば当然なんですけど.ここではRubyの拡張に使うことが多いだろうと予測される機能を中心に紹介します.
Rubyに機能を追加する
Rubyで提供されている関数を使えばRubyインタプリタに新しい機能を追加することができます.Rubyでは以下の機能を追加する関数が提供されています.
-
クラス,モジュール
-
メソッド,特異メソッドなど
-
定数
では順に紹介します.
クラス/モジュール定義
クラスやモジュールを定義するためには,以下の関数を使います.
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)
メソッド/特異メソッド定義
メソッドや特異メソッドを定義するには以下の関数を使います.
void rb_define_method(VALUE klass, const char *name,
VALUE (*func)(ANYARGS), int argc)
void rb_define_singleton_method(VALUE object, const char *name,
VALUE (*func)(ANYARGS), 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)(ANYARGS), int argc)
void rb_define_protected_method(VALUE klass, const char *name,
VALUE (*func)(ANYARGS), 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)(ANYARGS), int argc)
関数的メソッド(Kernel モジュールのprivate method)を定義するための関数は以下の通りです.
void rb_define_global_function(const char *name, VALUE (*func)(ANYARGS), 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 はクラスを引数として受け取って,新しく割り当てられたインスタンスを返さなくてはなりません.このインスタンスは,外部リソースなどを含まない,できるだけ「空」のままにしておいたほうがよいでしょう.
継承したクラスにある既存のメソッドをオーバーライドしているなら,オーバーライドされたメソッドを呼び出すには以下の関数を使います.
VALUE rb_call_super(int argc, const VALUE *argv)
現在のスコープのレシーバは(他に方法がなければ),以下の関数で得ることができます.
VALUE rb_current_receiver(void)
定数定義
拡張ライブラリが必要な定数はあらかじめ定義しておいた方が良いでしょう.定数を定義する関数は二つあります.
void rb_define_const(VALUE klass, const char *name, VALUE val) void rb_define_global_const(const char *name, VALUE val)
前者は特定のクラス/モジュールに属する定数を定義するもの,後者はグローバルな定数を定義するものです.
Rubyの機能をCから呼び出す
既に『1.5 Rubyのデータを操作する』で一部紹介したような関数を使えば,Rubyの機能を実現している関数を直接呼び出すことが出来ます.
# このような関数の一覧表はいまのところありません.ソースを見# るしかないですね.
それ以外にもRubyの機能を呼び出す方法はいくつかあります.
Rubyのプログラムをevalする
CからRubyの機能を呼び出すもっとも簡単な方法として,文字列で与えられたRubyのプログラムを評価する以下の関数があります.
VALUE rb_eval_string(const char *str)
この評価は現在の環境で行われます.つまり,現在のローカル変数などを受け継ぎます.
評価は例外を発生するかもしれないことに注意しましょう. より安全な関数もあります.
VALUE rb_eval_string_protect(const char *str, int *state)
この関数はエラーが発生するとnilを返します.そして,成功時には*stateはゼロに,さもなくば非ゼロになります.
IDまたはシンボル
Cから文字列を経由せずにRubyのメソッドを呼び出すこともできます.その前に,Rubyインタプリタ内でメソッドや変数名を指定する時に使われているIDについて説明しておきましょう.
IDとは変数名,メソッド名を表す整数です.Rubyの中では
:識別子
または
:"任意の文字列"
でアクセスできます.Cからこの整数を得るためには関数
rb_intern(const char *name) rb_intern_str(VALUE name)
を使います.Rubyから引数として与えられたシンボル(または文字列)をIDに変換するには以下の関数を使います.
rb_to_id(VALUE symbol) rb_check_id(volatile VALUE *name) rb_check_id_cstr(const char *name, long len, rb_encoding *enc)
もし引数がシンボルでも文字列でもなければ,to_str メソッドで文字列に変換しようとします.第二の関数はその変換結果を*nameに保存し,その名前が既知のシンボルでない場合は0を返します.この関数が0以外を返した場合は*nameは常にシンボルか文字列であり,0を返した場合は常に文字列です.第三の関数はRubyの文字列ではなくNUL終端されたCの文字列を使います.
Rubyから引数として与えられたシンボル(または文字列)をシンボルに変換するには以下の関数を使います.
rb_to_symbol(VALUE name) rb_check_symbol(volatile VALUE *namep) rb_check_symbol_cstr(const char *ptr, long len, rb_encoding *enc)
これらの関数は,IDの代わりにシンボルを返すことを除けば上記の関数と同じです.
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_funcallv(VALUE recv, ID mid, int argc, VALUE *argv) VALUE rb_apply(VALUE recv, ID mid, VALUE args)
applyには引数としてRubyの配列を与えます.
変数/定数を参照/更新する
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 定数定義』で紹介されている関数を使ってください.
RubyとCとの情報共有
C言語とRubyの間で情報を共有する方法について解説します.
Cから参照できるRubyの定数
以下のRubyの定数はCのレベルから参照できます.
QtrueQfalse-
真偽値.C言語から見た「
true」と「false」. Qnil-
C言語から見た「
nil」.
RTEST(obj)というマクロは obj が Qfalse か Qnil のとき0を返します.
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 の仕様は次の通りです.
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);
CのデータをRubyオブジェクトにする
Cの世界で定義されたデータ(構造体)をRubyのオブジェクトとして取り扱いたい場合がありえます.このような場合はTypedData_XXX マクロ群を用いて構造体へのポインタとRubyのオブジェクトとを互いに変換できます.
構造体からオブジェクトへ
構造体へのポインタ sval をRubyオブジェクトに変換するには次のマクロを使います.
TypedData_Wrap_Struct(klass, data_type, sval)
このマクロの戻り値は生成されたオブジェクトを表すVALUE値です.
このマクロは RUBY_TYPED_EMBEDDABLE フラグを持つ data_type に対しては使えません.そのような型には TypedData_Make_Struct やrb_data_typed_object_zalloc を使ってください.
klass はこのオブジェクトのクラスです.klass は, Object クラスから派生し, 必ず rb_define_alloc_func か rb_undef_alloc_func を呼び出してallocatorを設定してください.
data_type はこの構造体をRubyが管理するための情報を記述したconst rb_data_type_t型へのポインタです.
rb_data_type_t は概ね次のように定義されています.将来の使用のために予約されているフィールドは省略されています.
typedef struct rb_data_type_struct rb_data_type_t;
struct rb_data_type_struct {
const char *wrap_struct_name;
struct {
void (*dmark)(void*);
void (*dfree)(void*);
size_t (*dsize)(const void *);
/* reserved fields */
} function;
const rb_data_type_t *parent;
void *data;
VALUE flags;
};
wrap_struct_name はこの構造体を識別する名前です.主に統計情報の収集と出力に用いられます.プロセス内で一意であることが望ましいですが,特にCやRubyの識別子として有効である必要はありません.
dmark および dfree 関数はGC実行中に呼び出されます. なお, GC実行中はRubyオブジェクトのアロケーションは禁止されます. よって, dmark および dfree 関数でRubyオブジェクトのアロケーションは行わないでください.
dmark はガーベージコレクタがオブジェクトへの参照をマークするときに用いる関数です.この構造体がRubyのオブジェクトへの参照を保持するときには, dmark では rb_gc_mark などを用いて構造体内のすべての参照をマークしなければなりません.そのような参照を含まない時には0を指定します.
dfree はこの構造体がもう不要になった時に呼ばれる関数です.この関数がガーベージコレクタから呼ばれます.これがRUBY_DEFAULT_FREE の場合は,単純に構造体が解放されます.
dsize は構造体が消費しているメモリのバイト数を返す関数です.引数として構造体へのポインタが渡されます.実装困難であれば0 を渡しても差し支えありませんが, できるだけ指定するようにしてください.
dcompact はメモリコンパクションが行われるときに呼び出されます.rb_gc_mark_movable()によってマークされた参照対象の Ruby オブジェクトは,ここでrb_gc_location()に基づいて更新することができます.
parent はこの構造体が継承する別の構造体に対応する rb_data_type_t を指定します.その場合TypedData_Get_Struct()が派生オブジェクトも受け付けるようになります.
data にはユーザー定義の任意の値を指定できます.Rubyはこの値には関知しないので,好きに使ってください.
flags には次のフラグのうち当てはまるもののビット和を指定します.いずれもRubyのガーベージコレクタについての深い理解を必要としますので,良くわからない場合には0を指定すると良いでしょう.
RUBY_TYPED_FREE_IMMEDIATELY-
このフラグを指定すると,ガーベージコレクタはこの構造体が不要になった場合にはGC中に直ちに
dfreeを呼び出します.dfreeがRuby内部のロック(GVL)を解放する可能性がない場合はこのフラグを指定できます.指定しない場合は
dfree呼び出しは遅延され, ファイナライザと同じタイミングで実行されます. RUBY_TYPED_WB_PROTECTED-
オブジェクトの実装がライトバリアをサポートしていることを示します.このフラグを指定するとRubyはそのオブジェクトに対してGCをより効率的に実行できます.ただし,指定する場合はユーザーはそのオブジェクトのすべてのメソッドの実装に適切にライトバリアを挿入する責任があります.さもなくばRubyは実行時にクラッシュする可能性があります.
ライトバリアについては世代別GC も参照してください.
RUBY_TYPED_FROZEN_SHAREABLE-
オブジェクトがfreezeされていればRactor間で共有できることを示します.Appendix F. Ractor supportを参照してください.
このフラグがセットされていなければ,
Ractor.make_shareable()で共有可能にはできません. RUBY_TYPED_EMBEDDABLE-
このフラグは,RubyがC構造体を
mallocで別に割り当てるのではなく,オブジェクトのスロット内に格納してもよいことを示します.ただし,これは保証されるものではありません.Rubyはオブジェクトを埋め込まないことを選択する場合もあります.例えば,そのオブジェクトが大きすぎて,利用可能なスロットサイズのいずれにも収まらない場合などです.C構造体をオブジェクトスロット内に埋め込むことで,ポインタの追跡や
mallocによるオーバーヘッドが削減され,スイープのパフォーマンスが向上します.場合によっては,オブジェクトのメモリ使用量も削減できることがあります.埋め込み可能となるためには,型はいくつかの制限に従う必要があります:
-
C構造体へのポインタ,またはC構造体内部へのポインタは,GCのコンパクションが発生すると無効になるため,保存してはなりません.ただし,Ruby オブジェクトがスタック上に残っている限り,そのようなポインタを渡したり使用したりすることは有効です.
ある意味,これはスタック上に割り当てられた構造体に対する制限と似ています.
オブジェクトがコンパクションによって移動されたり解放されたりしないことを保証するには,
RB_GC_GUARDマクロを使用する必要があります.ただし,オブジェクトがRuby から C へ直接引数として渡される場合,すなわちrb_define_methodなどで使用される関数のパラメータとして渡される場合は除きます. -
DATA_PTRやRTYPEDDATA_DATA,TypedData_Wrap_Structマクロは使用できません.埋め込み可能なオブジェクトに対しては,RTYPEDDATA_GET_DATAまたはTypedData_Get_Structマクロのみ使用可能です.また,‘RTYPEDDATA(obj)->data` へのアクセスも無効です. -
dfree関数は,C構造体自体を解放してはなりません.dfreeをRUBY_DEFAULT_FREEに設定しても問題ありません.この機能を持たない古いバージョンのRubyをサポートするために,RUBY_TYPED_EMBEDDABLEが定義されていない場合に限り,C構造体を条件付きで解放することができます. -
型には
RUBY_TYPED_FREE_IMMEDIATELYフラグが設定されている必要があります.
埋め込みC構造体のサイズが可変である場合,
TypedData_Make_Structの代わりにrb_data_typed_object_zallocを使用できます.RUBY_TYPED_EMBEDDABLEの使用方法に関する注釈付きの例については,Embedded TypedData を参照してください. -
上記以外のフィールドは将来の使用のために予約されており、通常は初期化子は省略されます。ごく稀なケースですが,動的に rb_data_type_t 自体を確保する場合は,0クリアしてください.
このマクロは例外を発生させる可能性があることに注意してください. ラップされる sval が,解放する必要があるリソース (割り当てられたメモリ,外部ライブラリからのハンドルなど) を保持している場合は,rb_protect を使用する必要があります.あるいは,まずデータポインタを NULL に設定してオブジェクトを作成し,その後リソースを割り当ててそのオブジェクトに格納します.
Cの構造体の割当と対応するオブジェクトの生成を同時に行うマクロとして以下のものが提供されています.
TypedData_Make_Struct(klass, type, data_type, sval)
このマクロの戻り値は生成されたオブジェクトのVALUE値です.このマクロは以下の式のように働きます:
(sval = ZALLOC(type), TypedData_Wrap_Struct(klass, data_type, sval))
ただし,単にメモリを割り当てるだけなら,上記のコードのような「割り当て後にラップする」方法ではなく,このマクロを使用すべきです.なぜなら,「ラップ」の際に NoMemoryError が発生する可能性があり,その場合は sval がメモリリークとなるからです.
klass, data_type は Data_Wrap_Struct と同じ働きをします.type は割り当てるC構造体の型です.割り当てられた構造体は変数 sval に代入されます.この変数の型は type* である必要があります.
単純なメモリ割り当て以外の場合も同様です:
obj = TypedData_Wrap_Struct(klass, data_type, NULL); sval = allocate_some_resource(); if (success(sval)) RTYPEDDATA_DATA(obj) = sval;
sval がポインタ型でない場合は,void*にキャストする必要があるかもしれません.
宣言的マーク/コンパクト構造体参照
構造体の参照しているRubyオブジェクトが,条件付きロジックや複雑なデータ構造でラップされていない,単純な参照だけの場合は,VALUEへの参照のオフセット(edge)によってマーク付けと参照更新を宣言することもできます.
これにより,dmark や dcompact コールバック関数を定義せずに済みます.
参照が配置されている構造体内のオフセットへのVALUEポインタの静的リストを定義し,この参照リストを指すように data メンバーを設定します. 参照リストは RUBY_END_REFS で終わる必要があります.
以下のマクロが使用できます:
RUBY_TYPED_DECL_MARKING-
ruby_data_type_tがedgeを使用していることを示すフラグ RUBY_REFERENCES(ref_list_name)-
ref_list_name を参照リストとして定義する
RUBY_REF_END-
参照リストの終端
RUBY_REF_EDGE(struct, member)-
struct から member をedgeとして宣言する.
RUBY_REFERENCESのブロックの中で使用する. RUBY_REFS_LIST_PTR-
参照リストを
dmarkとして使用できるように変換する
以下は dir.c で定義されている Dir の例です.
// ラップされる構造体.3つのメンバの内,2番目が他のrubyオブジェクトへの参照
struct dir_data {
DIR *dir;
const VALUE path;
rb_encoding *enc;
}
// `path` エントリを持つ参照リスト `dir_refs` を定義
// RUBY_REF_ENDで終了していなければならない
RUBY_REFERENCES(dir_refs) = {
RUBY_REF_EDGE(dir_data, path),
RUBY_REF_END
};
// RUBY_TYPED_DECL_MARKINGをセットして定義された参照リストを"dmark"に使用しているので,
// マーク用コールバック関数は不要
static const rb_data_type_t dir_data_type = {
"dir",
{RUBY_REFS_LIST_PTR(dir_refs), dir_free, dir_memsize,},
0, NULL, RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_DECL_MARKING
};
このように,単純な参照を宣言的に宣言することで,GCがマークや移動,コンパクション中の参照の自動更新などをできるようになります.
オブジェクトから構造体へ
TypedData_Wrap_Struct や TypedData_Make_Struct で生成したオブジェクトから構造体へのポインタを復元するには以下のマクロを用います.
TypedData_Get_Struct(obj, type, &data_type, sval)
Cの構造体へのポインタは変数 sval に代入されます.
これらのマクロの使い方はちょっと分かりにくいので,後で説明する例題を参照してください.
例: dbmの拡張ライブラリの作成
ディレクトリを作る
% mkdir ext/dbm
Rubyに静的にリンクする場合にはRubyを展開したディレクトリの下,extディレクトリの中に拡張ライブラリ用のディレクトリを作る必要があります.名前は適当に選んで構いません.
設計する
まあ,当然なんですけど,どういう機能を実現するかどうかまず設計する必要があります.どんなクラスをつくるか,そのクラスにはどんなメソッドがあるか,クラスが提供する定数などについて設計します.
Cコードを書く
拡張ライブラリ本体となるC言語のソースを書きます.C言語のソースがひとつの時には「ライブラリ名.c」を選ぶと良いでしょう.また,後述する mkmf ライブラリのいくつかの関数がコンパイルを要するテストのために「conftest.c」というファイル名を使用することに注意してください.ソースファイル名として「conftest.c」を使用してはなりません.
Rubyは拡張ライブラリをロードする時に「+Init_ライブラリ名+」という関数を自動的に実行します.dbmライブラリの場合「Init_dbm」です.この関数の中でクラス,モジュール,メソッド,定数などの定義を行います.dbm.cから一部引用します.
#include <ruby.h>
void
Init_dbm(void)
{
/* DBMクラスを定義する */
VALUE cDBM = rb_define_class("DBM", rb_cObject);
/* DBMはEnumerableモジュールをインクルードする */
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ではTypedData_Make_Structを以下のように使っています.
struct dbmdata {
int di_size;
DBM *di_dbm;
};
static const rb_data_type_t dbm_type = {
"dbm",
{0, free_dbm, memsize_dbm,},
0, 0,
RUBY_TYPED_FREE_IMMEDIATELY,
};
obj = TypedData_Make_Struct(klass, struct dbmdata, &dbm_type, dbmp);
ここでは dbmdata 構造体へのポインタをRubyオブジェクトにカプセル化しています.DBM*を直接カプセル化しないのはclose()した時の処理を考えてのことです.
Rubyオブジェクトから dbmdata 構造体のポインタを取り出すために以下のマクロを使っています.
#define GetDBM(obj, dbmp) do {\
TypedData_Get_Struct((obj), struct dbmdata, &dbm_type, (dbmp));\
if ((dbmp) == 0) closed_dbm();\
if ((dbmp)->di_dbm == 0) closed_dbm();\
} while (0)
ちょっと複雑なマクロですが,要するにdbmdata構造体のポインタの取り出しと,closeされているかどうかのチェックをまとめているだけです.
DBMクラスにはたくさんメソッドがありますが,分類すると3種類の引数の受け方があります.ひとつは以下のように引数の数が固定のものです.
static VALUE
fdbm_aref(VALUE obj, VALUE keystr)
{
struct dbmdata *dbmp;
GetDBM(obj, dbmp);
/* Use dbmp to access the key */
dbm_fetch(dbmp->di_dbm, StringValueCStr(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への参照に値を代入してくれます.
引数の数をチェックするだけなら rb_check_arity() が使えます.これは引数をリストとして扱いたいときに便利です.
引数を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)
またはそのオブジェクト自身を登録してください.
void rb_gc_register_mark_object(VALUE object)
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[, target_prefix]): Makefileの生成
以下の変数を使うことができます.
$CFLAGS: コンパイル時に追加的に指定するフラグ(-Oなど) $CPPFLAGS: プリプロセッサに追加的に指定するフラグ(-Iや-Dなど) $LDFLAGS: リンク時に追加的に指定するフラグ(-Lなど) $objs: リンクされるオブジェクトファイル名のリスト
オブジェクトファイルのリストは,通常はソースファイルを検索して自動的に生成されますが,makeの途中でソースを生成するような場合は明示的に指定する必要があります.
ライブラリをコンパイルする条件が揃わず,そのライブラリをコンパイルしない時にはcreate_makefileを呼ばなければMakefileは生成されず,コンパイルも行われません.
dependを用意する
もし,ディレクトリにdependというファイルが存在すれば,Makefileが依存関係をチェックしてくれます.
% gcc -MM *.c > depend
などで作ることが出来ます.あって損は無いでしょう.
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が生成されますので,このステップは不要です.
makeする
動的リンクライブラリを生成する場合にはその場でmakeしてください.必要であれば make install でインストールされます.
ext以下にディレクトリを用意した場合は,Rubyのディレクトリでmakeを実行するとMakefileを生成からmake,必要によってはそのモジュールのRubyへのリンクまで自動的に実行してくれます.extconf.rbを書き換えるなどしてMakefileの再生成が必要な時はまたRubyディレクトリでmakeしてください.
拡張ライブラリはmake installでRubyライブラリのディレクトリの下にコピーされます.もし拡張ライブラリと協調して使うRubyで記述されたプログラムがあり,Rubyライブラリに置きたい場合には,拡張ライブラリ用のディレクトリの下に lib というディレクトリを作り,そこに 拡張子 .rb のファイルを置いておけば同時にインストールされます.
デバッグ
まあ,デバッグしないと動かないでしょうね.ext/Setupにディレクトリ名を書くと静的にリンクするのでデバッガが使えるようになります.その分コンパイルが遅くなりますけど.
できあがり
後はこっそり使うなり,広く公開するなり,売るなり,ご自由にお使いください.Rubyの作者は拡張ライブラリに関して一切の権利を主張しません.
Appendix A. Rubyのソースコードの分類
Rubyのソースはいくつかに分類することが出来ます.このうちクラスライブラリの部分は基本的に拡張ライブラリと同じ作り方になっています.これらのソースは今までの説明でほとんど理解できると思います.
Rubyのヘッダファイル
$repo_root/include/ruby以下はすべてmake installでインストールされます.拡張ライブラリからは,#include <ruby.h>でインクルードする必要があります.rbimpl_,RBIMPL_のプレフィックスが付いた実装の詳細のためのシンボルを除き,すべてのシンボルは公開APIです.
拡張ライブラリで直接インクルードできるのは,$repo_root/include/ruby/*.hのうち,対応するHAVE_RUBY_*_Hマクロが$repo_root/include/ruby.hヘッダーで定義されているものです.
Ruby言語のコア
- class.c
-
クラスとモジュール
- error.c
-
例外クラスと例外機構
- gc.c
-
記憶領域管理
- load.c
-
ライブラリのロード
- object.c
-
オブジェクト
- variable.c
-
変数と定数
Rubyの構文解析器
- parse.y
-
字句解析器と構文定義
- parse.c
-
自動生成
- defs/keywords
-
予約語
- lex.c
-
自動生成
- id.c
-
定義済みID
Rubyの評価器 (通称YARV)
- eval.c
- eval_error.c
- eval_jump.c
-
評価機
- compile.c
-
ASTからVM命令へのコンパイラ
- 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
-
VM実装
- insns.def
-
仮想機械語の定義
- defs/opt_insns_unif.def
-
命令融合
- defs/opt_operand.def
-
最適化のための定義
- -> insn*.inc opt*.inc vm.inc
-
自動生成
正規表現エンジン (鬼雲)
- 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
-
miniruby用
- inits.c
- main.c
- ruby.c
- version.c
-
Rubyコマンド
- builtin.c
- gem_prelude.rb
- prelude.rb
-
起動時に実行されるRubyコード用
クラスライブラリ
- array.c
- bignum.c
-
Integer(Bignum) - compar.c
- complex.c
- cont.c
- dir.c
- enum.c
- enumerator.c
- file.c
- hash.c
- io.c
- marshal.c
- math.c
- numeric.c
- pack.c
- proc.c
- process.c
-
Process
- random.c
-
乱数 (
Random) - range.c
- rational.c
- re.c
- signal.c
- sprintf.c
- string.c
- struct.c
- symbol.c
- time.c
- defs/known_errors.def
-
Errnoの下の例外クラス- -> known_errors.inc
-
自動生成
多言語化
- encoding.c
- transcode.c
- enc/*.c
-
エンコーディングクラス群
- enc/trans/*
-
コードポイント対応表
gorubyコマンドの実装
- goruby.c
-
gorubyコマンド
- 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 に代入するマクロ.
型チェック
RB_TYPE_P(value, type)-
value の内部的な型が type (
T_NIL,T_FIXNUM, etc.)か? TYPE(value)-
内部的な型 (
T_NIL,T_FIXNUM, etc.) FIXNUM_P(value)-
value が Fixnum か?
NIL_P(value)-
value が
nilか? RB_INTEGER_TYPE_P(value)-
value が
Integerか? RB_FLOAT_TYPE_P(value)-
value が
Floatか? void Check_Type(VALUE value, int type)-
value の内部的な型が type でなければ
TypeErrorを発生させる
型変換
FIX2INT(value), INT2FIX(i) FIX2LONG(value), LONG2FIX(l) NUM2INT(value), INT2NUM(i) NUM2UINT(value), UINT2NUM(ui) NUM2LONG(value), LONG2NUM(l) NUM2ULONG(value), ULONG2NUM(ul) NUM2LL(value), LL2NUM(ll) NUM2ULL(value), ULL2NUM(ull) NUM2OFFT(value), OFFT2NUM(off) NUM2SIZET(value), SIZET2NUM(size) NUM2SSIZET(value), SSIZET2NUM(ssize) rb_integer_pack(value, words, numwords, wordsize, nails, flags), rb_integer_unpack(words, numwords, wordsize, nails, flags) NUM2DBL(value) rb_float_new(f) RSTRING_LEN(str) RSTRING_PTR(str) StringValue(value) StringValuePtr(value) StringValueCStr(value) rb_str_new2(s)
クラス/モジュール定義
VALUE rb_define_class(const char *name, VALUE super)-
super のサブクラスであるRubyクラスをトップレベルの定数 name として定義する.
VALUE rb_define_class_under(VALUE module, const char *name, VALUE super)-
super のサブクラスであるRubyクラスを module の定数 name として定義する.
VALUE rb_define_module(const char *name)-
Rubyモジュールをトップレベルの定数 name として定義する.
VALUE rb_define_module_under(VALUE module, const char *name)-
Rubyモジュールを module の定数 name として定義する.
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 が呼ばれる.これらの関数は
rb_define_hooked_variableと同じですが,var 引数は意味がない. void rb_define_hooked_variable(const char *name, VALUE *var, VALUE (*getter)(), void (*setter)())-
関数によってhookのつけられたグローバル変数を定義する.変数が参照された時には getter が,関数に値がセットされた時には setter が呼ばれる.getter や setter に0を指定した時にはhookを指定しないのと同じ事になる.
VALUE getter(ID id, VALUE *var) void setter(VALUE val, ID id, VALUE *var)
void rb_global_variable(VALUE *var)-
マークする必要のあるRubyオブジェクトを含む大域変数を,GC によって解放されないように保護する.
void rb_gc_register_mark_object(VALUE object)-
マークする必要のあるRubyオブジェクトを,GCによって解放されないように登録する.
定数
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)(ANYARGS), int argc)-
メソッドを定義する.argc は
selfを除く引数の数.argc が-1の時, 関数には引数の数(selfを含まない)を第1引数, 引数の配列を第2引数とする形式で与えられる(第3引数はself).argc が-2の時,第1引数がself, 第2引数が args(引数を含むRubyの配列)という形式で与えられる. rb_define_private_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)-
privateメソッドを定義する.引数は
rb_define_method()と同じ. rb_define_singleton_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)-
特異メソッドを定義する.引数は
rb_define_method()と同じ. rb_check_arity(int argc, int min, int max)-
引数の数である argc が
min..maxの範囲に入っているかをチェックします.もし max がUNLIMITED_ARGUMENTSなら,上限はチェックしません.もし argc が範囲外ならArgumentErrorが発生します. rb_scan_args(int argc, VALUE *argv, const char *fmt, ...)-
argc, argv 形式で与えられた指定されたフォーマットに従って引数を分解し,続くVALUEへの参照にセットします.このフォーマットは,ABNFで記述すると以下の通りです.
scan-arg-spec := param-arg-spec [option-hash-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 option-hash-arg-spec := sym-for-option-hash-arg 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-option-hash-arg := ":" ; オプションハッシュを取得する ; ための指定; 省略不能な引数の ; 数よりも多くの引数が指定され, ; 最後の引数がハッシュ(または ; #to_hashで変換可能)の場合に ; 取得される.最後の引数がnilの ; 場合,可変長引数指定がなく, ; 省略不能引数の数よりも多くの ; 引数が指定された場合に取得される sym-for-block-arg := "&" ; イテレータブロックを取得するための ; 指定フォーマットが“12”の場合,引数は最低1つで,3つ(1+2)まで許されるという意味になります.従って,フォーマット文字列に続いて3つのVALUEへの参照を置く必要があります.それらには取得した変数がセットされます.変数への参照の代わりにNULLを指定することもでき,その場合は取得した引数の値は捨てられます.なお,省略可能引数が省略された時の変数の値は
nil(C言語のレベルではQnil)になります.返り値は与えられた引数の数です.オプションハッシュおよびイテレータブロックは数えません.
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)-
キーワードで指定された値を table にしたがって取り出します.table の最初の required 個のIDは必須キーワードを表し,続く optional (optional が負の場合は
-optional-1) 個のIDは省略可能キーワードです.必須キーワードが keyword_hash 中にない場合,“missing keyword”ArgumentErrorが発生します.省略可能キーワードがない場合は,values 中の対応する要素にはQundefがセットされます.keyword_hash に使用されない要素がある場合は,optional が負なら無視されますが,そうでなければ“unknown keyword”ArgumentErrorが発生します. VALUE rb_extract_keywords(VALUE *original_hash)-
original_hash で参照されるHashオブジェクトから,Symbolであるキーとその値を新しいHashに取り出します.original_hash の指す先には,元のHashがSymbol以外のキーを含んでいた場合はそれらがコピーされた別の新しいHash,そうでなければ0が保存されます.
Rubyメソッド呼び出し
VALUE rb_funcall(VALUE recv, ID mid, int narg, ...)-
メソッド呼び出し.文字列からmidを得るためには
rb_intern()を使う.private/protectedなメソッドでも呼び出せる. VALUE rb_funcall2(VALUE recv, ID mid, int argc, VALUE *argv)VALUE rb_funcallv(VALUE recv, ID mid, int argc, VALUE *argv)-
メソッド呼び出し.引数を argc, argv 形式で渡す.private/protectedなメソッドでも呼び出せる.
VALUE rb_funcallv_public(VALUE recv, ID mid, int argc, VALUE *argv)-
メソッド呼び出し.publicなメソッドしか呼べない.
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 のインスタンス変数 name を val にセットする.
制御構造
VALUE rb_block_call(VALUE obj, ID mid, int argc, VALUE * argv, VALUE (*func) (ANYARGS), VALUE data2)-
func をブロックとして設定し,obj をレシーバ,argc と argv を引数として mid メソッドを呼び出す.func は第一引数に
yieldされた値,第二引数に data2 を受け取る.複数の値がyieldされた場合(Cではrb_yield_values()とrb_yield_values2(),rb_yield_splat()),data2 はArrayとしてパックされている.第三, 第四引数の argc とargv によってyieldされた値を取り出すことができる. - [OBSOLETE]
VALUE rb_iterate(VALUE (*func1)(), VALUE arg1, VALUE (*func2)(), VALUE arg2) -
func2 をブロックとして設定し, func1 をイテレータとして呼ぶ.func1 には arg1 が引数として渡され, func2 には第1引数にイテレータから与えられた値, 第2引数に arg2が渡される.
1.9で
rb_iterateを使う場合は, func1 の中でRubyレベルのメソッドを呼び出さなければならない. 1.9でobsoleteとなった. 代わりにrb_block_callが用意された. VALUE rb_yield(VALUE val)-
val を値としてイテレータブロックを呼び出す.
VALUE rb_rescue(VALUE (*func1)(ANYARGS), VALUE arg1, VALUE (*func2)(ANYARGS), VALUE arg2)-
関数 func1 を arg1 を引数に呼び出す.func1の実行中に例外が発生した時には func2 を arg2 を第一引数, 発生した例外オブジェクトを第二引数として呼ぶ.戻り値は例外が発生しなかった時は func1の戻り値, 例外が発生した時には func2 の戻り値である.
VALUE rb_ensure(VALUE (*func1)(ANYARGS), VALUE arg1, VALUE (*func2)(ANYARGS), VALUE arg2)-
関数 func1 を arg1 を引数として実行し, 実行終了後(たとえ例外が発生しても) func2 を arg2 を引数として実行する.戻り値は func1の戻り値である(例外が発生した時は戻らない).
VALUE rb_protect(VALUE (*func) (VALUE), VALUE arg, int *state)-
関数 func を arg を引数として実行し, 例外が発生しなければその戻り値を返す.例外が発生した場合は,
*stateに非0をセットしてQnilを返す.rb_jump_tag()を呼ばずに捕捉した例外を無視する場合には,rb_set_errinfo(Qnil)でエラー情報をクリアしなければならない. void rb_jump_tag(int state)-
rb_protect()やrb_eval_string_protect()で捕捉された例外を再送する.state はそれらの関数から返された値でなければならない.この関数は直接の呼び出し元に戻らない. void rb_iter_break()-
現在の最も内側のブロックを終了する.この関数は直接の呼び出し元に戻らない.
void rb_iter_break_value(VALUE value)-
現在の最も内側のブロックを value で終了する.ブロックは引数で与えられた value を返す.この関数は直接の呼び出し元に戻らない.
例外・エラー
void rb_warning(const char *fmt, ...)-
rb_verbose時に標準エラー出力に警告情報を表示する.引数はprintf()と同じ. void rb_raise(rb_eRuntimeError, const char *fmt, ...)-
RuntimeError例外を発生させる.fmt 以下の引数は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, ...)-
インタープリタなどプログラムのバグでしか発生するはずのない状況の時呼ぶ.インタープリタはコアダンプし直ちに終了する.例外処理は一切行なわれない.
注意: "%"PRIsVALUEが Object#to_s(‘+’フラグが指定されているときは Object#inspect)を使ったVALUEの出力に利用できる.これは"%i"と衝突するため,整数には"%d"を使用すること.
Rubyの初期化・実行
Rubyをアプリケーションに埋め込む場合には以下のインタフェースを使う.通常の拡張ライブラリには必要ない.
void ruby_init()-
Rubyインタプリタの初期化を行なう.
void *ruby_options(int argc, char **argv)-
Rubyインタプリタのコマンドライン引数の処理を行ない,Rubyのソースコードをコンパイルする.コンパイルされたソースへのポインタ,もしくは特殊値を返す.
int ruby_run_node(void *n)-
コンパイルされたコードを実行する.実行に成功した場合はEXIT_SUCCESSを,エラーが起こったときはそれ以外を返す.
void ruby_script(char *name)-
Rubyのスクリプト名($0)を設定する.
インタプリタのイベントのフック
void rb_add_event_hook(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data)-
指定されたインタプリタのイベントに対するフック関数を追加します.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, VALUE data, VALUE self, ID id, VALUE klass)rb_add_event_hook()の第3引数 data は,フック関数の第2引数として渡されます.– これは1.8では現在のNODEへのポインタでした.++ 以下のRB_EVENT_HOOKS_HAVE_CALLBACK_DATAも参照してください. int rb_remove_event_hook(rb_event_hook_func_t func)-
指定されたフック関数を削除します.
メモリ使用量
void rb_gc_adjust_memory_usage(ssize_t diff)-
登録された外部のメモリ使用量を調整します.この関数で外部のライブラリがどのくらいメモリを使っているのかをGCに伝えることができます.正のdiff でこの関数を呼び出すとメモリ使用量の増加を意味します.新しいメモリブロックが確保されたり,ブロックがより大きなサイズで再割り当てされたりした場合などです.負の diff でこの関数を呼び出すとメモリ使用量の減少を意味します.メモリブロックが解放されたり,メモリブロックがより小さいサイズで再確保されたりした場合などです.この関数はGCを引き起こすかもしれません.
互換性のためのマクロ
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-
メソッド名を返すメソッド, Kernel#methods,
Object#singleton_methodsなどがSymbolを返すことを意味する. HAVE_RUBY_*_H-
ruby.h で定義されている.対応するヘッダが提供されていることを意味する.たとえば,
HAVE_RUBY_ST_Hが定義されている場合は単なる st.h ではなくruby/st.h を使用する.これらのマクロに対応するヘッダーファイルは,拡張ライブラリから直接インクルードしてもよい.
RB_EVENT_HOOKS_HAVE_CALLBACK_DATA-
rb_add_event_hook()がフック関数に渡す data を第3引数として受け取ることを意味する.
Appendix C. extconf.rbで使える関数たち
extconf.rbの中では利用可能なコンパイル条件チェックの関数は以下の通りである.
マクロ名に含まれる{VAR}は, var の英小文字を大文字に,英数字以外の文字をアンダースコアに置換したものを示す.
have_macro(macro, headers)-
ヘッダファイル header をインクルードしてマクロ macro が定義されているかどうかチェックする.マクロが定義されている時
trueを返す. have_library(lib, func)-
関数 func を定義しているライブラリ lib の存在をチェックする.チェックに成功すると,lib をリンクするための指定を$libsに追加し,
trueを返す. find_library(lib, func, path...)-
関数 func を定義しているライブラリ lib の存在を,ライブラリパスにpath を追加しながらチェックする.チェックに成功すると,lib をリンクするための指定を$libsに追加し,
trueを返す. have_func(func, header)-
ヘッダファイル header をインクルードして関数 func の存在をチェックする.func が標準ではリンクされないライブラリ内のものである時には先に
have_libraryでそのライブラリをチェックしておく事.チェックに成功すると,プリプロセッサマクロHAVE_{FUNC}を定義し,trueを返す. have_var(var, header)-
ヘッダファイル header をインクルードして変数 var の存在をチェックする.var が標準ではリンクされないライブラリ内のものである時には先に
have_libraryでそのライブラリをチェックしておく事.チェックに成功すると,プリプロセッサマクロHAVE_{VAR}を定義し,trueを返す. have_header(header)-
ヘッダファイルの存在をチェックする.チェックに成功すると,プリプロセッサマクロ
HAVE_{HEADER_H}を定義し,trueを返す. find_header(header, path...)-
ヘッダファイル header の存在を,インクルードパスに path を追加しながらチェックする.チェックに成功すると,プリプロセッサマクロ
HAVE_{HEADER_H}を定義し,trueを返す. have_struct_member(type, member[, header[, opt]])-
ヘッダファイル header をインクルードして,メンバ member を持つ集合型 type が存在するかをチェックする.チェックに成功すると,プリプロセッサマクロ
HAVE_{TYPE}_{MEMBER}を定義し,trueを返す. have_type(type, header, opt)-
ヘッダファイル header をインクルードして型 type が存在するかをチェックする.チェックに成功すると,プリプロセッサマクロ
HAVE_TYPE_{TYPE}を定義し,trueを返す. check_sizeof(type, header)-
ヘッダファイル header をインクルードして型 type の
char単位のサイズを調べる.チェックに成功すると,プリプロセッサマクロSIZEOF_{TYPE}を定義し,そのサイズを返す.定義されていないときはnilを返す. append_cppflags(array-of-flags[, opt])append_cflags(array-of-flags[, opt])append_ldflags(array-of-flags[, opt])-
各 flag が使用可能であれば,それぞれ$CPPFLAGS,$CFLAGS, $LDFLAGSに追加する.コンパイラのフラグには移植性がないので,変数に直接追加せずこれらを使うことが望ましい.
create_makefile(target[, target_prefix])-
拡張ライブラリ用の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, option=nil)-
pkg-configコマンドからパッケージpkgの情報を[cflags, ldflags, libs]の配列として得る.$CFLAGS,$LDFLAGS,$libsにはそれぞれの値が追加される.pkg-configの実際のコマンドは,以下の順で試される.-
コマンドラインで
--with-pkg-config=command オプションが指定された場合: command option -
pkg
-configoption -
pkg-configoption pkg
option が指定された場合は,上記の配列の代わりにそのオプションを指定して得られた出力をstripしたものを返す.
-
Appendix D. 世代別GC
Ruby 2.1から世代別GCに対応しました.我々はこれをRGenGCと呼んでいます.RGenGCは,過去の拡張ライブラリに(ほぼ)互換性を保つように開発されているため,拡張ライブラリ側の対応はほぼ不要です.
ただし,対応をすることで性能を向上することができる可能性があります.もし拡張ライブラリに高い性能が必要である場合は対応を検討して下さい.
とくにRARRAY_PTR()/RHASH_TBL()のようなマクロを用いてポインタに直接アクセスするようなコードは書かないようにして下さい.代わりに,rb_ary_aref(), rb_ary_store() などの,適切な API 関数を利用するようにして下さい.
そのほか,対応についての詳細は Appendix D. Generational GC を参照して下さい.
Appendix E. Ractor サポート
Ruby 3.0 から,Ruby プログラムを並列に実行するための仕組みである Ractor が導入されました.適切に並列に実行するためには,Ractor サポートが必要になります.サポートしていないライブラリは,メイン Ractor 以外で実行するとエラーになります(Ractor::UnsafeError).
Ractor をサポートするための詳細は,Appendix F. Ractor support を参照してください.