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.