Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to rb_protect everything in ruby

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?

like image 410
johannes Avatar asked Jun 18 '12 15:06

johannes


People also ask

Does Ruby have protected?

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.

What is protected method in Ruby?

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 .

What is private and protected in Ruby?

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.


1 Answers

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.

like image 198
sfstewman Avatar answered Dec 06 '22 05:12

sfstewman