保持Ruby C扩展代码DRY?

时间:2014-01-04 13:06:00

标签: c ruby refactoring stack dry

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

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

从我发现的情况来看,与实际的Ruby代码相比,这种方法相当慢(多达0.1秒)而且根本不是DRY。有什么办法可以把它提取出来,用于某种全局变量或函数吗?我尝试将这两行放在任何函数之外但我需要使用rb_iv_get(self, "@iv_name")访问self,这样就会抛出错误。

1 个答案:

答案 0 :(得分:1)

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

但是,如果您需要将Ruby对象作为C扩展的一个组成部分进行管理,那么您可以将该值保留为C struct的一部分,并为Ruby方实现一个访问器,以便查看它必要。然后,您根本无需致电rb_iv_get即可访问C中的VALUE

首先,您需要了解如何使用ruby.h中的Data_Wrap_StructData_Get_Struct宏 - 使用CD Jukebox示例Extending Ruby chapter of Programming Ruby是查看此操作的好地方

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

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

除了CD Jukebox示例之外,您还需要实现一个将some_ruby_val标记为正在使用的函数(如果您不这样做,Ruby的垃圾收集器将回收内存,您可以结束指向其他一些对象 - 通常会让你产生段错误:

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

当你使用Data_Wrap_Struct时,你会告诉它关于这个函数,以及链接中记录的destroy函数:

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;
}