how to rb_protect everything in ruby
Asked Answered
A

1

6

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?

Ales answered 18/6, 2012 at 15:56 Comment(0)
V
4

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.

Vescuso answered 27/2, 2013 at 6:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.