instance_eval from C ext

J

Joel VanderWerf

I'm stumped... how do I call instance_eval from a C extension?

I have an object and a proc:

VALUE obj, pr;

and I want to eval pr with obj as self.

Currently, I do something like this, which works, but it's inefficient:

rb_funcall(obj, rb_intern("insteval_proc"), 1, pr);

and I have the following defined in ruby:

class Object
def insteval_proc pr
instance_eval &pr
end
end

I'd rather avoid the extra method call.

It doesn't seem like I can use rb_obj_instance_eval(), because that
takes string arguments, not block arguments. Of course, if you have a
block accompanying the method call, it will instance_eval that, but I
don't know how to set that up in C. That's why I do "instance_eval &pr"
in my little hack.

And I can't see how to use rb_iterate(). This is what I tried:


static VALUE my_instance_eval(VALUE obj)
{
return rb_obj_instance_eval(0, 0, obj);
}

static VALUE call_block(VALUE arg1, VALUE block)
{
return rb_funcall(block, ID_call, 0);
}

//...
rb_iterate(my_instance_eval, obj, call_block, pr);


but I get "block not supplied (ArgumentError)", so I guess it's not
being propagted into the rb_funcall.

Is there a simple way to emulate "&block" in C?

Even better, is there a way to rebind the proc's self to my object, so I
only have to do it once, and can just call the block instead of using
instance eval?
 
J

Joel VanderWerf

Any ideas, anyone? Is this something you can do in ruby, but not using
the C API directly?
 
J

Joel VanderWerf

Mauricio said:

Well, rb_iterate() is what I was trying before, but apparently it does
not arrange for rb_block_given_p() to be true, and so
rb_obj_instance_eval() bails out. I've adapted your example to show how
that is the case in both your code and mine.

The output (ruby-1.9.0 snapshot) is:

foo2, block given = 0
call_gsub, block given = 0
block2, block given = 0
Inside block, with string: h.
block2, block given = 0
Inside block, with string: e.
block2, block given = 0
Inside block, with string: l.
block2, block given = 0
Inside block, with string: l.
block2, block given = 0
Inside block, with string: l.
"HELLo, worLd!"
instance_eval_proc, block given = 0
my_instance_eval, block given = 0
example.rb:11:in `instance_eval_proc': block not supplied (ArgumentError)
from example.rb:11


So I'm still looking for a solution to my problem, which is: how to
execute a Proc in the context of a given self object, from C code.

In other words, given the following working ruby code:

class Object
def instance_eval_proc(pr)
instance_eval(&pr)
end
end

obj = [1,2,3]
pr = proc { reverse }

p obj.instance_eval_proc(pr) # ==> [3, 2, 1]

how would you rewrite #instance_eval_proc in C?

== extconf.rb ==

require "mkmf"

create_makefile("example")

== example.rb ==

require "example.so"

b = "hello, world!"
CTest.foo2(b, /[ehl]/)
p b


obj = [1,2,3]
pr = proc { reverse }

p obj.instance_eval_proc(pr)

== example.c ==


#include <ruby.h>
#include <stdio.h>

VALUE mCTest;

static
VALUE
block2(VALUE str)
{
printf("block2, block given = %d\n", rb_block_given_p());
Check_Type(str, T_STRING);
printf("Inside block, with string: %s.\n", RSTRING(str)->ptr);
return rb_funcall(str, rb_intern("upcase"), 0);
}

static
VALUE
call_gsub(VALUE args)
{
VALUE str, re;
printf("call_gsub, block given = %d\n", rb_block_given_p());
str = rb_ary_entry(args, 0);
re = rb_ary_entry(args, 1);

return rb_funcall(str, rb_intern("gsub!"), 1, re);
}

static
VALUE
foo2(VALUE self, VALUE str, VALUE re)
{
VALUE args;
printf("foo2, block given = %d\n", rb_block_given_p());
args = rb_ary_new();
rb_ary_push(args, str);
rb_ary_push(args, re);

return rb_iterate(call_gsub, args, block2, str);
}

static VALUE my_instance_eval(VALUE obj)
{
printf("my_instance_eval, block given = %d\n", rb_block_given_p());
return rb_obj_instance_eval(0, 0, obj);
}

static VALUE call_block(VALUE arg1, VALUE block)
{
printf("call_block, block given = %d\n", rb_block_given_p());
return rb_funcall(block, rb_intern("call"), 0);
}

static VALUE instance_eval_proc(VALUE self, VALUE pr)
{
printf("instance_eval_proc, block given = %d\n", rb_block_given_p());
return rb_iterate(my_instance_eval, self, call_block, pr);
}

void
Init_example()
{
mCTest = rb_define_module("CTest");
rb_define_singleton_method(mCTest, "foo2", foo2, 2);
rb_define_method(rb_cObject, "instance_eval_proc", instance_eval_proc, 1);
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,009
Latest member
GidgetGamb

Latest Threads

Top