2014年4月17日木曜日

mrubyで環境を持つCの関数のprocを生成する

mrubyに、環境を持つCの関数からmrubyのプロシジャを生成するAPIと、取り出す関数が追加された:
mrb_proc_new_cfunc_with_env
mrb_cfunc_env_get

Add API to define cfunc Proc with userdata. · bf6b1df · mruby/mruby

mrubybindではC++のテンプレートを使って、Cの関数を呼び出すためのバインダ関数を自動的に生成することで、mrubyからCの関数を呼び出せるようにしている。バインダ関数は、mrubyから呼び出された引数をC言語の値に変換して、元のCの関数を呼び出し、結果をmrubyの値に変換する。で、このバインダ関数は元のCの関数を知っている必要があるんだけど、上のAPIが追加されるまではmrubyでCの関数が環境を持つことができなかったので、直接は実現できなかった。

そこで以前は、自動的に生成されるバインダの関数の他に、mrubyのdefine_methodでmrubyの関数も生成して、そいつが元のCの関数を保持しておき、バインダ関数に渡すことで実現していた。こうすると実行時にmruby側からCの関数を呼びだそうとした場合には、
  1. define_methodで生成されたmrubyの関数
  2. テンプレートで生成されたバインダ関数
  3. 元のCの関数
と、3段階の関数呼び出しが必要になってしまっていた。

上のAPIを使ってバインダ関数に環境を保持するようにして、
  1. テンプレートで生成されたバインダ関数
  2. 元のCの関数
と、2段階の関数呼び出しに減らすことができた。

またそれにより、mrubybindがバインド時に必要だったmrubyで実装していた補助モジュールが一切必要なくなり、Cから呼び出せるAPIだけで実現できるようになった。

Enjoy!