Cに組み込んだmrubyのコードのエラーハンドリングについて
「ミドルウェアにmrubyを組み込む方法」についてまとめた - Kentaro Kuribayashi's blogの続き的な話題。Cのプログラムに組み込んだmrubyのコードを評価した時に、そのコードがなんらかのエラーを起こした時にどうするか。 ### mrubyのコードを評価するAPI まず、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);
}