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.