I want to call ruby code from my own C code. In case an exception gets raised, I have to rb_protect the ruby code I call. rb_protect looks like this:
VALUE rb_protect(VALUE (* proc) (VALUE), VALUE data, int * state)
So proc
has to be a function which takes VALUE
arguments and returns VALUE
. I have to call a lot of functions which do not work that way. How can I rb_protect
them from raising exceptions?
I have thought of using Data_Make_Struct
to wrap everything into one ruby object and call methods on it. Data_Make_Struct
could itself raise an exception. How do I rb_protect
Data_Make_Struct
?
In Ruby, a protected method (or protected message handler) can only respond to a message with an implicit/explicit receiver (object) of the same family. It also cannot respond to a message sent from outside of the protected message handler context.
A protected method is like a private method in that it can only be invoked from within the implementation of a class or its subclasses. It differs from a private method in that it may be explicitly invoked on any instance of the class, and it is not restricted to implicit invocation on self .
protected methods can be called by any instance of the defining class or its subclasses. private methods can be called only from within the calling object. You cannot access another instance's private methods directly.
To use rb_protect
in a flexible way (e.g., to call a Ruby function with an arbitrary numbers of arguments), pass a small dispatch function to rb_protect
. Ruby requires that sizeof(VALUE) == sizeof(void*)
, and rb_protect
blindly passes the VALUE
-typed data to the dispatch function without inspecting it or modifying it. This means that you can pass whatever data you want to the dispatch function, let it unpack the data and call the appropriate Ruby method(s).
For example, to rb_protect
a call to a Ruby method, you might use something like this:
#define MAX_ARGS 16
struct my_callback_stuff {
VALUE obj;
ID method_id;
int nargs;
VALUE args[MAX_ARGS];
};
VALUE my_callback_dispatch(VALUE rdata)
{
struct my_callback_stuff* data = (struct my_callback_stuff*) rdata;
return rb_funcall2(data->obj, data->method_id, data->nargs, data->args);
}
... in some other function ...
{
/* need to call Ruby */
struct my_callback_stuff stuff;
stuff.obj = the_object_to_call;
stuff.method_id = rb_intern("the_method_id");
stuff.nargs = 3;
stuff.args[0] = INT2FIX(1);
stuff.args[1] = INT2FIX(2);
stuff.args[2] = INT2FIX(3);
int state = 0;
VALUE ret = rb_protect(my_callback_dispatch, (VALUE)(&stuff), &state);
if (state) {
/* ... error processing happens here ... */
}
}
Also, keep in mind that rb_rescue
or rb_ensure
may be a better approach for some problems.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With