Search code examples
crubyruby-c-extension

How do I convert a Proc to a block in a Ruby C extension?


I am storing an array of procs in a Ruby C extension and I need to go through and instance_eval each proc. The problem is that instance_eval only accepts blocks, not procs. This is not an issue in Ruby where I can simply go:

proc_list.each { |my_proc|
    @receiver.instance_eval(&my_proc)
}

However I am unsure how to go about this using the Ruby C API.

Does anyone have any ideas how I might accomplish this?


Solution

  • From the pickaxe, p. 871 (1.9 edition)

    VALUE rb_iterate( VALUE (*method)(), VALUE args, VALUE (*block)(), VALUE arg2 )

    Invokes method with argument args and block block. A yield from that method will invoke block with the argument given to yield and a second argument arg2.

    So pass your Proc objects as arg2 and define a (*block)() function that just forwards the passed value to the Proc's #call method.

    Something like

    for (i = 0; i < numProcs; i++)
    {
      rb_iterate( forwarder, receiver, block, procs[i] );
    }
    
    /*...*/
    
    VALUE forwarder(VALUE receiver)
    {
      // the block passed to #instance_eval will be the same block passed to forwarder
      return rb_obj_instance_eval(0, NULL, receiver);
    }
    VALUE block(VALUE proc)
    {
      return rb_funcall(proc, rb_intern("call"), 0);
    }
    

    I haven't tested this code, but it's consistent with the caveats in this article.