Cに組み込んだmrubyのコードのエラーハンドリングについて
「ミドルウェアにmrubyを組み込む方法」についてまとめた - Kentaro Kuribayashi's blogの続き的な話題。Cのプログラムに組み込んだmrubyのコードを評価した時に、そのコードがなんらかのエラーを起こした時にどうするか。
mrubyのコードを評価するAPI
まず、mrubyのコードを評価するAPIには、コンテキスト(実行するコードのファイル、行などを示す情報)をわたさないものとわたすものとの2種類があります。
- コンテキストをわたさないもの
- 例:
mrb_value mrb_load_string(mrb_state *mrb, const char *s)
- 例:
- コンテキストをわたさすもの
- 例:
mrb_value mrb_load_string_cxt(mrb_state *mrb, const char *s, mrbc_context *c)
- 例:
とりあえず、まずはmrubyのコードを評価したいだけならどっちを使ってもOKです。しかし、入力されるmrubyのコードには、正常に終了する場合の他、エラーで終了する場合もあるため、それらのハンドリングをする必要があります。
コンテキストをわたさないAPIを使うと、上記のエラーのうち、コンパイルエラー時にSEGVになってしまうので(仕様というか、API利用時の制限?)、コンテキストをわたすものを使うのがよいでしょう。
エラーが発生したらどうするか
mrubyのコードを評価した結果が上記のようなエラーとなった場合、以下の値が帰ってきます。
- コンパイル時のエラー:
nil
(mrb_nil_value()
の返す値) - 実行時のエラー:
undef
(mrb_undef_value()
の返す値)
また、mrb->exc
にエラーの内容が代入されます。どっちであってもなんらかのエラーを示す型の値(=mrb_value
型の値)として扱えればいいということであれば、以下のようなコードを書けばいいことになります。
mrb_value value; mrb_state *mrb = mrb_open(); mrbc_context *cxt = mrbc_context_new(mrb); value = mrb_load_string_cxt(mrb, code, cxt); // undef: compile error // nil: runtime error if (mrb->exc != 0 && (mrb_nil_p(value) || mrb_undef_p(value))) { value = mrb_obj_value(mrb->exc); }
こうしておけば、エラーで終了することなく、変数value
になんらかのオブジェクトを示すmrb_value
型の値として、正常時・異常時どちらであっても返り値を記録しておくことができます。
まとめ
以上を、まとめると、以下のようなコードになるでしょう。その上で、得られたmrb_value
型の値を使っていくには、ミドルウェアにmrubyを組み込む方法の17ページ目以降を参照してください。
ちなみに、下記コードのtype
がどういう値を取るかは、include/mruby/value.hを参照のこと。
#include "mruby.h" #include "mruby/compile.h" #include "mruby/string.h" int main(int argc, char **argv) { char *code = argv[1]; // 本エントリの趣旨に関係ないので適当に処理 mrb_state *mrb = mrb_open(); mrbc_context *cxt = mrbc_context_new(mrb); mrb_value value = mrb_load_string_cxt(mrb, code, cxt); // 返り値が: // undef: compile error // nil: runtime error if (mrb->exc != 0 && (mrb_undef_p(value) || mrb_nil_p(value))) { value = mrb_obj_value(mrb->exc); } enum mrb_vtype type = mrb_type(value); printf("type: %d, Inspect: %s\n", type, mrb_str_to_cstr(mrb, mrb_inspect(mrb, value))); mrb->exc = 0; // このコードでは不要だが、`mrb`を使いまわす場合は戻しておく mrbc_context_free(mrb, cxt); mrb_close(mrb); }