RubyInline включает другой сгенерированный файл для вызова функции из этого файла - PullRequest
1 голос
/ 16 января 2012

У меня есть два расширения C для Ruby (для приложения Rails).Я инициализирую их в application.rb с config.after_initialize.

Вот первый класс:

class Object
  require 'inline'

  inline do |builder|

    builder.c <<-EOC
      static VALUE 
      rb_obj_is_number(){
        // code to return Qtrue or Qfalse
      }
    EOC

  end

  alias is_number rb_obj_is_number

end

Вот второй (который должен использовать rb_obj_is_number() сверху):

class Array
  require 'inline'

  inline do |builder|

    builder.c <<-EOC
      static VALUE 
      rb_ary_some_fun(){
        double result = 0;
        long i, len = RARRAY_LEN(self);
        VALUE *c_arr = RARRAY_PTR(self);

        for(i=0; i<len; i++) {
          if ( TYPE(rb_obj_is_number(c_arr[i])) == T_TRUE ) {       
            result += NUM2DBL(c_arr[i]);
          }
        }
        return rb_float_new(result);
      }
    EOC
  end

  alias some_fun rb_ary_some_fun

end

При попытке вызвать функцию из одного файла в другой я получаю следующую ошибку:

dyld: lazy symbol binding failed: Symbol not found: _rb_obj_is_number

Я думаю, это потому, что я не включил первый сгенерированный файл во второй,Как это сделать, чтобы компилятор распознал rb_obj_is_number?

1 Ответ

1 голос
/ 16 января 2012

Ваши функции объявлены как static в отдельных единицах компиляции, поэтому, конечно, rb_ary_some_fun не может видеть rb_obj_is_number.Сразу приходят на ум два варианта:

Один из вариантов - поместить реализацию C rb_obj_is_number в строку, доступную для обоих вызовов builder.c.Это даст вам две копии функции, но:

  • Обе реализации будут static, поэтому у вас не будет проблем с пространством имен.
  • Реализация, вероятно, достаточно мала, чтобылишний раздув не будет проблемой.
  • Обе реализации будут происходить из одной и той же строки, поэтому у вас не должно возникнуть проблем с их сохранением.

У вас есть что-товот так:

# In some library file...
module CUtil
  IS_NUMBER = %q{
    static VALUE rb_obj_is_number(void) { /*...*/ }
  }
end

# When monkey patching Object...
class Object
  require 'inline'
  inline do |builder|
    builder.c CUtil::IS_NUMBER
  end
  alias is_number rb_obj_is_number
end

# When monkey patching Array...
class Array
  require 'inline'
  inline do |builder|
    builder.c <<-EOC
      #{CUtil::IS_NUMBER}
      static VALUE rb_ary_some_fun(void) { /*...*/ }
    EOC
  end
  alias some_fun rb_ary_some_fun
end

Это большая куча клуджа, поэтому я бы не советовал.Вероятно, есть случаи, когда такой подход имеет смысл.

правильный способ сделать это - вызвать is_number как метод Ruby из вашего C. Вызов is_number как методпозволяет переопределять его, исправлять обезьяны и т. д., как и любой другой метод.Вы бы использовали rb_funcall что-то вроде этого:

/* There's no need to call this over and over again inside a loop. */
ID is_number = rb_intern("is_number");
/* ... */
if(rb_funcall(c_arr[i], is_number, 0) == Qtrue) {
    result += NUM2DBL(c_arr[i]);
}

Также я рекомендую использовать правильные сигнатуры функций (например, rb_obj_is_number(void)), чтобы ваш компилятор не попадал в режим K & R.Вам также следует настроить флажки предупреждений вашего компилятора, чтобы он громко жаловался на подобные вещи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...