crubyblockruby-1.9ruby-c-extension

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


I'm writing a Ruby 1.9 C extension and I want to do the following in ruby:

notifier = Notifier.new
notifier.on 'click' do
  puts "clicked!"
end

Now the problem with this is that on the C method, I only "receive" a block, and, as far as I know, it's not even a parameter: I just can call with with rb_yield.

So my question is: is there a way on a Ruby 1.9 C extension, to transform a block into a proc or something, so I can store it inside my module, and call it later whenever I want/need them? Like an async callback!

I already implemented this with Procs/lambdas, but it's just ugly not to use the block syntax directly.


Solution

  • In the Ruby C source you'll see this in proc.c:

    /*
     * call-seq:
     *   proc   { |...| block }  -> a_proc
     *
     * Equivalent to <code>Proc.new</code>.
     */
    
    VALUE
    rb_block_proc(void)
    {
        return proc_new(rb_cProc, FALSE);
    }
    

    and Proc.new does this:

    Creates a new Proc object, bound to the current context. Proc::new may be called without a block only within a method with an attached block, in which case that block is converted to the Proc object.

    So you'd do something like this:

    VALUE p = rb_block_proc();
    /* and then store `p` somewhere convenient */
    

    and then later on, to call the block/Proc:

    rb_funcall(p, rb_intern("call"), 0);
    

    That rb_funcall is pretty much the C version of p.send(:call).