Ben Thomas wrote:
There is a lot of function in the Ruby C API that takes the
VALUE (*body)(), VALUE args
combo parameters (such as rb_protect, or rb_rescue), and I don’t
understand how I should pass multiple arguments with such declaration.
For example, how should I rb_protect a function call like this?
rb_const_get( rb_cObject, rb_intern( "Foo" ) );
I saw other source cde like this:
VALUE args[2];
args[0] = rb_cObject;
args[1] = rb_intern( "Foo" );
rb_protect( rb_const_get, args, &result );
But this kind of code doesn’t works (and just don’t make sense for me
after what I read from eval.c, but maybe I’m missing something).
It is very non-obvious (at first glance) how functions like
rb_rescue() and rb_protect() work.
The second argument to rb_protect() is just a pointer to “some data”,
and the contents of that data are up to you. That data pointer will be
passed as the single argument to the function you pass as the first
argument to rb_protect(), so when you write:
rb_protect(func, data, &result);
you should understand that Ruby will execute the line:
func(data);
For the example you described, it’s not working because the function
rb_const_get() is expecting two arguments, not one. So what to do?
This usually means you’ll need to provide a “helper” function for the
function you’re actually trying to “protect”. Still using your example
scenario, let’s cook up a helper for rb_const_get() that takes just one
argument:
VALUE rb_const_get_helper(VALUE data)
{
VALUE *args = (VALUE *) data;
return rb_const_get(args[0], args[1]);
}
Here, we’re assuming that ‘data’ points to a C array of VALUEs, and that
array has two elements. This function just turns around and calls the
real function, rb_const_get(), with the correct number of arguments. To
activate this with rb_protect(), we can now use the original snippet you
posted:
VALUE args[2];
VALUE result;
int state;
args[0] = rb_cObject;
args[1] = rb_intern("Foo");
result = rb_protect(rb_const_get_wrapper, (VALUE) &args[0], &state);
Hope this helps,
Lyle