I'm trying to solve the problem of how to trap a Ruby exception from a
C call to a Ruby method. (Specifically, I'm writing server-side CORBA
bindings which unmarshal an incoming request and dynamically dispatch
it to a Ruby servant object registered with the ORB.) The code for
this is completely generic; it has no idea what methods it's calling
or what exceptions these might raise. What it needs to do is get a
copy of the exception raised so it can marshal it over the wire back
to the client. Right now I'm using rb_rescue for the method call,
along with a helper class to hold any exception raised. The code
looks like this:
class ErrorHolder
def initialize(error=nil)
unless error.nil?
raise TypeError unless error.kind_of?(StandardError)
end
@error = error
end
end
// wrapper for rb_apply, necessary since all arguments for a function
passed to rb_rescue must be packed into a single array
static VALUE
rorbit_apply_try(VALUE packed_args)
{
int argc;
VALUE *argv;
VALUE recv;
VALUE sym;
VALUE args;
Check_Type(packed_args, T_ARRAY);
argc = RARRAY(packed_args)->len;
argv = RARRAY(packed_args)->ptr;
rb_scan_args(argc, argv, "3", &recv, &sym, &args);
Check_Type(args, T_ARRAY);
return rb_apply(recv, SYM2ID(sym), args);
}
// Use as second function pointer to rb_rescue (with error_holder used
by caller to retrieve error value)
static VALUE
rorbit_trap_exception(VALUE error_holder, VALUE error)
{
if (!RTEST(rb_obj_is_kind_of(error, rb_eStandardError)))
rb_iv_set(error_holder, "@error", rb_class_new_instance(0, 0,
rb_eTypeError);
else
rb_iv_set(error_holder, "@error", error);
return Qnil;
}
This is the code that calls an arbitrary method on the Ruby servant
using rb_apply:
// use rb_rescue to catch exception
VALUE packed_args = rb_ary_new3(3, servant->impl,
ID2SYM(rb_intern(name)), args);
VALUE error_holder = rb_class_new_instance(0, 0,
rorbit_c_CORBA_ORBit_ErrorHolder);
results = rb_rescue(rorbit_apply_try, packed_args,
rorbit_trap_exception, error_holder);
error = rb_iv_get(error_holder, "@error");
Is there a way to retrieve an exception from the method called by
rb_apply without having to use a helper class? I was frankly
surprised there wasn't a simpler way to do it, since this is something
C extensions would have to do all the time. Am I missing something
obvious?