如何使用c扩展名扩展我的ruby类?

时间:2011-06-22 15:58:51

标签: c ruby inline ruby-c-extension

如果我有用Ruby编写的Foo :: Bar,我想将一个方法添加到Bar作为C扩展。现在,当我在C中创建Foo :: Bar时:

static VALUE Foo;
static VALUE Bar;

static VALUE 
print_string(VALUE self, VALUE string) {
  printf("%s", StringValuePtr(string));
  return Qnil;
}

void Init_foo() {
    Foo = rb_define_module("Foo");
    Bar = rb_define_class_under(Foo, "Bar", rb_cObject);
    rb_define_method(Bar, "print_string", print_string, 1);
}

但问题是:

ruby-1.9.2-p180 :001 > require 'ext/foo'   #=> ["Foo"]
ruby-1.9.2-p180 :002 > f = Foo::Bar.new   #=> #<Foo::Bar:0x000001046bce48>
ruby-1.9.2-p180 :003 > f.original_ruby_method
NoMethodError: undefined method `original_ruby_method' for #<Foo::Bar:0x000001046bce48>

所以我基本上覆盖了原来的Foo :: Bar。如何扩展它而不是覆盖它?

3 个答案:

答案 0 :(得分:3)

我找到了解决这个问题的方法。

void Init_foo() {
    rb_eval_string("require './lib/foo'");
    VALUE Bar = rb_path2class("Foo::Bar");
    rb_define_method(Bar, "print_string", print_string, 1);
}

答案 1 :(得分:1)

您的解决方案的替代方案,您需要来自C扩展的Ruby代码,将需要Ruby代码的扩展。

require 'foo.so'require 'ext/foo.so'(取决于您编译的库的最终位置)添加到lib/foo.rb,然后在客户端代码中正常调用require 'foo'(假设{{ 1}}在您的加载路径上。)

我认为这样做是更清晰,更常见。

请注意,即使您的平台生成了其他内容,也可以使用lib后缀,即当实际文件为.so时,它仍将在Mac上运行。

答案 2 :(得分:1)

一个隐藏的函数 - 我不得不深入挖掘源代码,但你可以使用rb_const_get来获取对现有模块和类的引用。

void Init_foo() {
  Foo = rb_const_get( rb_cObject, rb_intern("Foo");
  Bar = rb_const_get( Foo, rb_intern("Bar");
  rb_define_method(Bar, "print_string", print_string, 1);
}

如果要确保创建类/模块(如果它不存在):

void Init_foo() {
  if ( rb_const_defined( rb_cObject, rb_intern("Foo") ) )
    Foo = rb_const_get( rb_cObject, rb_intern("Foo");
  else
    Foo = rb_define_module("Foo");

  if ( rb_const_defined( Foo, rb_intern("Bar") ) )
    Bar = rb_const_get( Foo, rb_intern("Bar");
  else
    Bar = rb_define_class_under(Foo, "Bar", rb_cObject);

  rb_define_method(Bar, "print_string", print_string, 1);
}