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

问题描述 投票:0回答:3

如果我有用 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。如何扩展它而不覆盖它?

c ruby inline ruby-c-extension
3个回答
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
投票

您的解决方案的另一种选择是需要来自 C 扩展的 Ruby 代码,即需要来自 Ruby 代码的扩展。

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

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

请注意,即使您的平台生成其他内容,您也可以使用

.so
后缀,即当实际文件是
.bundle
时,它仍然可以在 Mac 上运行。


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);
}
© www.soinside.com 2019 - 2024. All rights reserved.