Everynow and again I wind up rewriting a ruby performance hotspot in C. It happens infrequently enough that I always forget the C api for passing a block implemented in C to some ruby code. Hopefully writing this down will help me remember this in the future. Today, I wanted to call
find_each on a class, using a C function as the block. Pre ruby 1.9 you need to call
rb_iterate which always did my head in, but in 1.9 you use
rb_block_call which is way more straightforward (
rb_iterate is still there but deprecated)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
block_method is the C function that implements my block.
some_method is the function that wishes to call
klass passing the block.
rb_block_call takes the 4th argument you give it and sets things up so that the ruby method call made will use that method as a block. The last argument passed to
rb_block_call is some context that is the second argument to your block method. On MRI you seem to be able to use an arbitrary pointer (rather than a ruby object) but this doesn’t work on rubinius.
The first 4 parameters are straight forward: the object to call the method on, what method to call and the arguments for that method, expressed as a length and an array of VALUE (here I am passing just one argument, 0, as an example).
If you block yields more than one value then they will be in the argc & argv parameters in block_method. As I understand it, yielded_object will always be the same as argv