繁体   English   中英

如何使用交流电扩展来扩展我的红宝石课程?

[英]How do I extend my ruby class with a 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。 如何扩展它而不覆盖它?

我想出了一种解决此问题的方法。

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

解决方案的替代方法是,需要C扩展中的Ruby代码,则需要Ruby代码中的扩展。

lib/foo.rb require 'foo.so'lib/foo.rb require 'ext/foo.so' (取决于编译后的库在哪里结束),然后在客户端代码中正常调用lib/foo.rb require 'foo' (假设lib是在您的加载路径上)。

我认为以这种方式进行操作更加清晰和普遍。

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

一个隐藏了一些功能的函数-我必须深入研究源代码才能找到它,但是您可以使用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);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM