Ruby C API の例外処理
概要
次のようにCを介してRubyスクリプトを実行しています。
#include <ruby.h>
int main(void) {
ruby_init();
int status;
rb_load_protect(rb_str_new2("./test.rb"), 0, &status);
if (status) {
VALUE rbError = rb_funcall(rb_gv_get("$!"), rb_intern("message"), 0);
printf("%s\n", StringValueCStr(rbError));
}
ruby_finalize();
return status;
}
Ruby スクリプト:
1.hello
今実行すると、次の出力が得られます。
undefined method `hello' for 1:Integer
これは予想通りです。 次に、このメソッドを定義します。
VALUE hello(VALUE self) {
return Qnil;
}
// just after ruby_init()
rb_define_method(rb_cInteger, "hello", hello, 0);
今実行すると、次のようになります
ruby: [BUG] Segmentation fault at 0x00000000000000b0
/* ... */
ここでエラーを処理するにはどうすればよいでしょうか?この場合は nil ですが、理由はわかりません。
解決策
ステータスを初期化するだけでよいと思います。
int status = 0;
rb_load_protect は、例外がある場合はステータスを設定しますが、例外がない場合はステータスを 0 に設定しないように見えますが、それに &status を渡すことで、コンパイラからの初期化された変数の警告を抑制しています。
現時点で起こっていることは、ステータスが何らかの (ゼロではない) ガベージとして開始され、例外がない限りゼロに設定されないため、if ブロックは常に実行されます。ただし、例外がない場合は nil なので、メッセージを呼び出すとハンドルされない例外が発生し、クラッシュが発生します。