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 find_each
on 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[0]