Skip to content

Instantly share code, notes, and snippets.

@yugui
Last active May 27, 2023 08:36
Show Gist options
  • Save yugui/87ef6964d8a76794be6f to your computer and use it in GitHub Desktop.
Save yugui/87ef6964d8a76794be6f to your computer and use it in GitHub Desktop.
extension.ja.rdoc -- extension library

CのデータをRubyオブジェクトにする

Cの世界で定義されたデータ(構造体)をRubyのオブジェクトとして取り扱いたい場合がありえます.このような場合はTypedData_XXX マクロ群を用いて構造体へのポインタとRubyのオブジェクトとを互いに変換できます.

# 古い(非Typedな)Data_XXXマクロ群は非推奨になりました.

# 将来のバージョンのRubyでは古いマクロは動作しなくなる可能性があります.

構造体からオブジェクトへ

構造体へのポインタsvalをRubyオブジェクトに変換するには次のマクロを使います。

TypedData_Wrap_Struct(klass, data_type, sval)

このマクロの戻り値は生成されたオブジェクトを表すVALUE値です.

klassはこのオブジェクトのクラスです.data_typeはこの構造体をRubyが管理するための情報を記述したconst rb_data_type_t型へのポインタです.

なお, klassは, Objectや他のクラスではなくData (rb_cData)という特別なクラスから派生することが推奨されます.Dataから派生しない場合には, 必ずrb_undef_alloc_func(klass) を呼び出してください.

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 *);
                void *reserved[2];
        } 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はこの構造体がもう不要になった時に呼ばれる関数です.この関数がガーベージコレクタから呼ばれます.これが-1の場合は,単純に構造体が解放されます.

dsizeは構造体が消費しているメモリのバイト数を返す関数です.引数として構造体へのポインタが渡されます.実装困難であれば0を渡しても差し支えありませんが, できるだけ指定するようにしてください.

reservedとparentは0で埋めなければなりません.

dataにはユーザー定義の任意の値を指定できます.Rubyはこの値には関知しないので,好きに使ってください.

flagsには次のフラグのうち当てはまるもののビット和を指定します.いずれもRubyのガーベージコレクタについての深い理解を必要としますので,良くわからない場合には0を指定すると良いでしょう.

RUBY_TYPED_FREE_IMMEDIATELY

このフラグを指定すると,ガーベージコレクタはこの構造体が不要になった場合にはGC中に直ちにdfreeを呼び出します.dfreeがRuby内部のロック(GVL)を解放する可能性がない場合はこのフラグを指定できます.

指定しない場合はdfree呼び出しは遅延され, ファイナライザと同じタイミングで実行されます.

RUBY_TYPED_WB_PROTECTED

オブジェクトの実装がライトバリアをサポートしていることを示します.このフラグを指定するとRubyはそのオブジェクトに対してGCをより効率的に実行できます.ただし,指定する場合はユーザーはそのオブジェクトのすべてのメソッドの実装に適切にライトバリアを挿入する責任があります.さもなくばRubyは実行時にクラッシュする可能性があります.

ライトバリアについては付録Dの「世代別GC」も参照してください.

Cの構造体の割当と対応するオブジェクトの生成を同時に行うマクロとして以下のものが提供されています.

TypedData_Make_Struct(klass, type, data_type, sval)

このマクロの戻り値は生成されたオブジェクトのVALUE値です.このマクロは以下の式のように働きます:

(sval = ZALLOC(type), TypedData_Wrap_Struct(klass, data_type, sval))

klass, data_typeはData_Wrap_Structと同じ働きをします.type は割り当てるC構造体の型です.割り当てられた構造体は変数sval に代入されます.この変数の型は (type*) である必要があります.

オブジェクトから構造体へ

TypedData_Wrap_StructやTypedData_Make_Structで生成したオブジェクトから構造体へのポインタを復元するには以下のマクロを用います.

TypedData_Get_Struct(obj, type, &data_type, sval)

Cの構造体へのポインタは変数svalに代入されます.

これらのマクロの使い方はちょっと分かりにくいので,後で説明する例題を参照してください.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment