http://storehouse.sakura.ne.jp/viewvc/viewvc.cgi/rua/rua/rua.c?root=svn&view=markup
ソース
static int rua_proc_call(lua_State *L) { VALUE proc, args, retval, error_handler, errargs, errmsg; int i, n, status; proc = (VALUE) lua_touserdata(L, lua_upvalueindex(1)); error_handler = (VALUE) lua_touserdata(L, lua_upvalueindex(2)); args = rb_ary_new(); n = lua_gettop(L); for (i = 0; i < n; i++) { rb_ary_push(args, rua_torbval(L, i + 1, error_handler)); } rb_ary_push(args, proc); retval = rb_protect(_rua_proc_call, args, &status); if (status != 0) { if (rb_obj_is_kind_of(error_handler, rb_cProc)) { errargs = rb_ary_new(); rb_ary_push(errargs, ruby_errinfo); rb_ary_push(errargs, error_handler); retval = rb_protect(_rua_proc_call, errargs, &status); if (status != 0) { errmsg = rb_check_convert_type(ruby_errinfo, T_STRING, "String", "to_s"); fprintf(stderr, "warning: %s\n", StringValuePtr(errmsg)); } } else { retval = Qnil; } } rua_pushrbval(L, retval, error_handler); return 1; }
Rubyで実行
require 'rua' rua = Rua.new(lambda {|e| puts <<-EOS | error handler called. | ----- | #{e.backtrace.join("\n | ")} | ----- EOS }) rua.openlibs(:all) rua[:rb_func] = lambda do |x| puts "rb_func(#{x}) called." raise 'xxxxx' end lua_func = rua.eval(<<EOS) rb_func('in lua') lua_func = function(x) print('lua_func(' .. x .. ') called.') end lua_func('in lua') return lua_func EOS rb_func = rua[:rb_func] lua_func.call("in ruby") rb_func.call("in ruby")
ハンドラを引数で持ちまわっているのが、ちょっとかっこ悪い…struct ruaを直接渡すようにすればよかったかも。
rb_func(in lua) called.
| error handler called.
| -----
| foo.rb:16
| foo.rb:19:in `call'
| foo.rb:19:in `eval'
| foo.rb:19
| -----
lua_func(in lua) called.
lua_func(in ruby) called.
rb_func(in ruby) called.
| error handler called.
| -----
| foo.rb:16
| foo.rb:32:in `call'
| foo.rb:32
| -----
ruby_errinfoはスレッドセーフって考えていいのかな?