rb_iterate replaced by rb_block_call API between Ruby 1.8 and 1.9

You may have noticed that some Ruby libraries using C extensions were suddenly having core dumps when compiled using Ruby 1.9, whereas compiling with Ruby 1.8 got no problems.
A good culprit might be the usage of rb_iterate method in the C extension: rb_iterate API has been deprecated and changed since Ruby 1.9, in favor of rb_block_call. In fact this new method is more intuitive and easy to use than its predecessor.
Although I have no problems with it being deprecated, I think that breaking its behavior was a rough move taken by Ruby developers.

I will illustrate the differences using the following code, written in Ruby, then in C using Ruby 1.8 rb_iterate API, and in C using Ruby 1.9 rb_block_call API:

# object, object_param and context_var are already initialized as 3 Ruby objects
object.method(object_param) do |yield_param|
  # Access yield_param and context_var
end
// iterate_args: Ruby Array containing arguments given as 2nd parameters of rb_iterate call
VALUE call_method_from_iterate(VALUE iterate_args) {
  VALUE object = rb_ary_entry(iterate_args, 0);
  VALUE object_param = rb_ary_entry(iterate_args, 1);
  return rb_funcall(object, rb_intern("method"), 1, object_param);
}

// yield_args: Ruby Array containing arguments given by the yield call (implemented in the "method" method of "object")
// context_args: Ruby Array containing arguments given as 4th parameters of rb_iterate call
VALUE yielded_block(VALUE yield_args, VALUE context_args) {
  VALUE yield_param = rb_ary_entry(yield_args, 0);
  VALUE context_var = rb_ary_entry(context_args, 0);
  // Access yield_param and context_var
}

// object, object_param and context_var are already initialized as VALUE
rb_iterate(
  call_method_from_iterate, // C method to be called once yield is ready to be used
  rb_ary_new3(2, // Ruby Array giving parameters to the previously defined C method
    object,
    object_param),
  yielded_block, // C method to be called when "yield" is called
  rb_ary_new3(1, // Ruby Array that will also be given to the yielded method: pass context parameters here
    context_var)
);
// yielded_object: First argument given to the yield call (implemented in the "method" method of "object"). Same as yield_argv[0].
// context_args: Ruby Array containing arguments given as 4th parameters of rb_iterate call
// yield_argc: Number of arguments given by the yield call
// yield_argv: C Array containing arguments given by the yield call
VALUE yielded_block(VALUE yielded_object, VALUE context_args, int yield_argc, VALUE yield_argv[]) {
  VALUE yield_param = yield_argv[0]; // Could be "= yielded_object" too
  VALUE context_var = rb_ary_entry(context_args, 0);
  // Access yield_param and context_var
}

// object, object_param and context_var are already initialized as VALUE
VALUE method_args[1];
method_args[0] = object_param;
rb_block_call(
  object, // Object to call the method on
  rb_intern("method"), // Method ID to be called on the object, once yield is ready to be used
  1, // Number of arguments given to the method
  method_args, // C Array of arguments given to the method
  RUBY_METHOD_FUNC(yielded_block), // C method to be called when "yield" is called
  rb_ary_new3(1, // Ruby Array that will also be given to the yielded method: pass context parameters here
    context_var)
);

We can see that using rb_block_call spares an extra method definition, and is more readable.

About Muriel Salvan

I am a freelance project manager and polyglot developer, expert in Ruby and Rails. I created X-Aeon Solutions and rivierarb Ruby meetups. I also give trainings and conferences on technical topics. My core development principles: Plugins-oriented architectures, simple components, Open Source power, clever automation, constant technology watch, quality and optimized code. My experience includes big and small companies. I embrace agile methodologies and test driven development, without giving up on planning and risks containment methods as well. I love Open Source and became a big advocate.
C, Howto, Ruby , , , , , , , , , , ,

Leave a Reply

Your email address will not be published. Required fields are marked *