保持Ruby C扩展代码DRY



我用C编写了一个简单的Stacks和Queues库,作为Ruby的扩展,每当我需要检索实例变量时(在本例中为stack.h),我都需要编写相同的两行代码:

VALUE stack;
stack = rb_iv_get(self, "@stack");

根据我的发现,与实际的Ruby代码相比,这种方法相当慢(长达0.1秒),而且一点也不DRY。有没有什么方法可以把它提取出来,变成某种全局变量或函数?我试着把这两行放在任何函数之外,但我需要用rb_iv_get(self, "@iv_name")访问self,所以这会导致一个错误。

我不认为您的速度问题与对rb_iv_get的调用直接相关。您应该首先考虑是否需要将数据保存在Ruby对象。将数据放在C数据结构中,并根据扩展添加到类中的方法的需要将其转换为Ruby数据或从Ruby数据转换出来,可能会更有效。

但是,如果您需要将Ruby对象作为C扩展的一个组成部分来管理,那么您可以将该值保留为Cstruct的一部分,并在必要时为Ruby端实现一个访问器来查看它。那么你根本不需要调用rb_iv_get来访问C.中的VALUE

首先,您需要了解如何使用ruby.h中的Data_Wrap_StructData_Get_Struct宏-编程ruby的扩展ruby章节是一个很好的地方,可以通过CD Jukebox示例来了解这一点。

然后,有了这个,你可以让你的一个结构组件成为一个价值:

typedef struct my_structure {
int some_number;
VALUE some_ruby_val;
} MyStructure;

除了CD Jukebox的例子外,您还需要实现一个将some_ruby_val标记为正在使用的函数(如果您不这样做,Ruby的垃圾收集器将回收内存,并且您可能会指向其他对象-通常这会导致segfault):

void my_structure_gc_mark( MyStructure *mine ) {
rb_gc_mark( mine->some_ruby_val );
return;
}

当你使用Data_Wrap_Struct时,你会告诉它这个功能,以及链接中记录的销毁功能:

Data_Wrap_Struct( klass, my_structure_gc_mark, my_structure_destroy, mine );

Ruby会在必要时负责调用它。

最后,要获得扩展持有的Ruby数据,只需打开该结构,并像struct的任何其他部分一样引用它。举个例子,下面是如何实现上面的访问器方法:

VALUE my_ext_get_some_ruby_val( VALUE self ) {
MyStructure *mine;
Data_Get_Struct( self, MyStructure, mine );
return mine->some_ruby_val;
}

最新更新