tags:

views:

212

answers:

1

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?

+1  A: 

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.

rampion
I'm not sure this is what i want; looking at README.EXT: VALUE rb_iterate(VALUE (*func1)(), void *arg1, VALUE (*func2)(), void *arg2)Calls the function func1, supplying func2 as the block. func1 will becalled with the argument arg1. func2 receives the value from yield asthe first argument, arg2 as the second argument. VALUE rb_yield(VALUE val)Evaluates the block with value val.So it appears that rb_iterate() lets me use another C function as a block argument, but I actually want a PROC as a block argument. I need it as a block arg so I can rb_obj_instance_eval it:)
banister
Like I said above, have the C function you pass just invoke Proc#call on the Proc object you pass as arg2.
rampion
banister
ok. I'll try again. `VALUE (*method()` should be a func that invokes `receiver#instance_eval`. `VALUE (*block)()` should be a func that invokes `arg2#call` and you make an `rb_iterate()` call for each member of `proc_list` with `arg2 = p`.
rampion
So that means that in the `Proc#call` the context will be set correctly.
rampion
if you use `rb_obj_instance_eval()`, then the block set for `VALUE (*method)()` will be passed down to the instance eval.
rampion