2014-03-02 46 views
3

我正在研究Programming Ruby 1.9的示例。是否有可能创建不暴露给Ruby的实例变量,仅在C中可见 - 例如初始化t_init中的C结构并在t_add中使用它?如何在C扩展中的Ruby层隐藏实例变量?

如果我在id_push下面声明结构或变量,它是类结构/变量,我需要实例。

是否有可能在不使用Data_Wrap_Struct/Data_Get_Struct或rb_iv_set/rb_iv_get的情况下实现,因为Ruby中不需要实例变量,仅从C中仅可见?

#include "ruby.h" 

static ID id_push; 

// how to define instance variables here? 

static VALUE t_init(VALUE self) { 
    VALUE arr; 
    arr = rb_ary_new(); 
    rb_iv_set(self, "@arr", arr); 
    return self; 
} 

static VALUE t_add(VALUE self, VALUE obj) { 
    VALUE arr; 
    arr = rb_iv_get(self, "@arr"); 
    rb_funcall(arr, id_push, 1, obj); 
    return arr; 
} 

VALUE cTest; 

void Init_my_test() { 
    cTest = rb_define_class("MyTest", rb_cObject); 
    rb_define_method(cTest, "initialize", t_init, 0); 
    rb_define_method(cTest, "add", t_add, 1); id_push = rb_intern("push"); 
} 

回答

3

你正在寻找的C函数是rb_ivar_setrb_ivar_get

rb_ivar_set有三个参数:

  1. VALUE型红宝石接收器对象
  2. ID型的ivar名称分配
  3. VALUE型红宝石右手侧对象。

rb_ivar_get有两个参数:

  1. VALUE型红宝石接收对象
  2. ID型伊娃名称检索

红宝石可以与多种内部存储实例变量ID-类型名称。然而,从Ruby层可见的唯一变量名是以@开始的变量名,例如, rb_intern("@foo")离开@使实例变量无法从Ruby层访问,但允许您从C层存储和访问它。


这是您的代码示例与此技术实施。

#include "ruby.h" 

static ID id_push; 

static VALUE t_init(VALUE self) { 
    VALUE arr; 
    arr = rb_ary_new(); 
    rb_ivar_set(self, rb_intern("arr"), arr); /* <-- */ 
    return self; 
} 

static VALUE t_add(VALUE self, VALUE obj) { 
    VALUE arr; 
    arr = rb_ivar_get(self, rb_intern("arr")); /* <-- */ 
    rb_funcall(arr, id_push, 1, obj); 
    return arr; 
} 

VALUE cTest; 

void Init_my_test() { 
    cTest = rb_define_class("MyTest", rb_cObject); 
    rb_define_method(cTest, "initialize", t_init, 0); 
    rb_define_method(cTest, "add", t_add, 1); id_push = rb_intern("push"); 
} 

测试吧!它应该像这样运行(我没编译上面的,有可能是拼写错误):

require 'my_test' 

class MyTest 
    def check 
    return @arr 
    end 
end 

t = MyTest.new 
t.add(1) #=> [1] 
t.check #=> nil 

#check方法去寻找@arr实例变量,并配备了两手空空。你的实例变量是安全可靠的,锁定在C层!

+1

这太可怕了:-)它可能会工作,但你确定GC会用这些部分可访问的东西做正确的事情吗?任何备受推崇的扩展库实际上是否使用此技术作为快捷方式(以隐藏由C添加的实例变量)? –

+1

好的,发现'rb_iv_set'和'rb_ivar_set'之间的区别,现在有意义(和+1),最初我以为你建议在没有'@'的情况下使用'rb_iv_set'。 。 。 –

+0

它的工作原理是,我现在可以将C结构保存为类实例变量,仅在C中可见,并且在shell尝试显示它们并且conf.echo处于活动状态时,irb中不会出现段错误。 Ruby垃圾收集器也会清理这些变量,或者? – bsrdjan