Как расширить класс ruby ​​с помощью ac? - PullRequest
2 голосов
/ 22 июня 2011

Если у меня Foo :: Bar, написанный на Ruby, и я хочу добавить метод в Bar как расширение C. Прямо сейчас, когда я создаю Foo :: Bar в C, как это:

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 ]

3 голосов
/ 22 июня 2011

Я нашел способ решить эту проблему.

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 голос
/ 08 марта 2012

Функция немного скрыта - мне пришлось копаться в источнике, чтобы найти ее, но вы можете использовать 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);
}
1 голос
/ 22 июня 2011

Альтернативой вашему решению, когда вам требуется код Ruby из расширения C, может быть требование расширения из кода Ruby.

Добавьте require 'foo.so' или require 'ext/foo.so' (в зависимости от того, где заканчивается ваша скомпилированная библиотека) к lib/foo.rb, затем в коде клиента просто вызовите require 'foo' как обычно (предполагая, что lib находится на вашем пути загрузки).

Я думаю, что делать это таким образом яснее и чаще.

Обратите внимание, что вы можете использовать суффикс .so, даже если ваша платформа производит что-то еще, то есть он все равно будет работать на Mac, когда фактическим файлом является .bundle.

...