如何在C扩展中隐藏Ruby层中的实例变量?

时间:2014-03-02 08:05:39

标签: ruby ruby-c-extension

我正在研究Programming Ruby 1.9的例子。是否可以创建未暴露给Ruby的实例变量,仅在C中可见 - 例如,在t_init中初始化C结构并在t_add中使用它?

如果我在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");
}

1 个答案:

答案 0 :(得分:3)

您正在寻找的C函数是 rb_ivar_set rb_ivar_get

rb_ivar_set有三个参数:

  1. VALUE - 类型的Ruby接收器对象
  2. 要分配的ID - 类型的ivar名称
  3. VALUE - 类型的Ruby右侧对象。
  4. rb_ivar_get有两个参数:

    1. VALUE - 类型的Ruby接收器对象
    2. 要检索的ID - 类型的ivar名称
    3. Ruby可以在内部存储具有多种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层中!