概要
Struct をメモリ空間に展開し、それに Ruby からアクセスする方法を紹介します
環境
- CentOS 7.5.1804
- Ruby 2.5.0p0
サンプルコード
NativeExtensions なのですべて C で書かれています
後述で説明します
#include <ruby.h>
typedef struct {
char *name;
int age;
} MyStruct;
static void my_struct_free(MyStruct *s) {
free(s);
}
static VALUE my_struct_alloc(VALUE self) {
MyStruct *s = ALLOC(MyStruct);
return Data_Wrap_Struct(self, 0, my_struct_free, s);
}
static VALUE wrap_set(VALUE self) {
MyStruct *s;
Data_Get_Struct(self, MyStruct, s);
s->name = (char *)"hawk";
s->age = 10;
return Qnil;
}
static VALUE wrap_show(VALUE self) {
MyStruct *s;
Data_Get_Struct(self, MyStruct, s);
printf("%s\n", s->name);
printf("%d\n", s->age);
return Qnil;
}
void Init_firstext(void) {
VALUE fe = rb_define_class("FirstExt", rb_cObject);
rb_define_alloc_func(fe, my_struct_alloc);
rb_define_method(fe, "set", wrap_set, 0);
rb_define_method(fe, "show", wrap_show, 0);
}
説明
まずは今回の肝となる Struct を用意します
Struct 内に Struct は持たないようにしています
typedef struct {
char *name;
int age;
} MyStruct;
Ruby 側で FirstExt.new
されたときに構造体の領域をメモリに確保する必要があります
そうしない状態で構造体にアクセスしようとすると Segmentation fault で落ちます
そのために rb_define_alloc_func(fe, my_struct_alloc);
を定義します
これは new された際に my_struct_alloc
メソットを呼び出し構造体のメモリ領域を確保します
my_struct_alloc
は以下の通りです
static VALUE my_struct_alloc(VALUE self) {
MyStruct *s = ALLOC(MyStruct);
return Data_Wrap_Struct(self, 0, my_struct_free, s);
}
決まり文句のような感じです
Data_Wrap_Struct
は C の構造体をラップして self クラスのインスタンスである Ruby オブジェクトを返します
これで構造体にアクセスできる準備はできました
Ruby から構造体にアクセスするためのメソッド set
, show
を用意します
そしてそれらは wrap_set
, wrap_show
を呼び出すように定義します
rb_define_method(fe, "set", wrap_set, 0);
rb_define_method(fe, "show", wrap_show, 0);
それぞれのメソッドを C 側で定義します
まずメモリ上に展開した構造体にアクセスするには Data_Get_Struct
を使います
ポインタを準備することでそのポインタにアドレスを設定してくれます
あとはアロー演算子を使って構造体のメンバにアクセスすれば OK です
ここでポイントなのは構造体を参照する関数は必ず static VALUE wrap_show(VALUE self)
というように引数と返り値に VALUE
を使う必要があります
set
は特に返り値はないですが、これを static void wrap_set(VALUE self)
のように宣言してしまうといざ set
をコールしたときに Segmentation fault が発生します
構造体自体をリターンするには
少し調べたのですが簡単にはいかなさそうです (というか無理そう?)
定義した構造体のメンバにアクセスするのが目的なのであれば各メンバを取得する Getter を定義してあげる感じになると思います
やり方がわかったら、また別の記事で招待したいと思います
最後に
NativeExtensions で Struct の基本的な扱い方を紹介しました
C 側にちゃんと領域を alloc してあげる必要があるのがポイントです
また構造体を参照する場合は Data_Get_Struct
を使いますがこれを使う関数は VALUE
な返り値と引数を必ず持つ点も重要です
0 件のコメント:
コメントを投稿