【问题标题】:How do I extend my ruby class with a c extension?如何使用 c 扩展扩展我的 ruby​​ 类?
【发布时间】:2011-06-22 15:58:50
【问题描述】:

如果我有 Foo::Bar 用 Ruby 编写,并且我想向 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。如何扩展而不覆盖它?

【问题讨论】:

  • 查看名为“RubyInline”的 gem。它被名为“image_science”的 gem 使用,它调用 c 代码来操作图像。
  • 是的,我知道 RubyInline,但宁愿不使用它,因为它不支持所有数据类型。我还将写很多 C,最好把它们分开。
  • 我假设您实际上已经在您的 Ruby 代码中的某处定义了 Foo::Bar#original_ruby_method - 这在您的示例中没有看到?

标签: c ruby inline ruby-c-extension


【解决方案1】:

我想出了解决这个问题的方法。

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

【讨论】:

    【解决方案2】:

    如果您需要 C 扩展中的 Ruby 代码,另一种解决方案是要求 Ruby 代码中的扩展。

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

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

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

    【讨论】:

    • 看起来确实更干净,但是当我尝试时,我得到:“NameError: Bar is already defined”
    • @Jeremy 我无法重现该错误 - 它是从哪一行提出来的?你的 require 'foo.so' 是在 rb 文件的顶部还是底部?
    • @Jeremy 这是我的,如果你想看看:gist.github.com/1041242
    【解决方案3】:

    一个有点隐藏的函数 - 我不得不深入研究源代码才能找到它,但您可以使用 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);
    }
    

    【讨论】:

    • 这是在现有常量上构建的正确方法。
    猜你喜欢
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多