RubyInline包含另一个生成的文件,用于从该文件调用函数

时间:2012-01-15 23:20:30

标签: c ruby

我有两个用于Ruby的C扩展(用于Rails应用程序)。我使用application.rbconfig.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 个答案:

答案 0 :(得分:1)

您的函数在单独的编译单元中声明为static,因此当然rb_ary_some_fun无法看到rb_obj_is_number。立即想到两个选项:

一种选择是将rb_obj_is_number的C实现放在两个builder.c调用都可访问的String中。这将为您提供该函数的两个副本,但是:

  • 这两个实现都是static,因此您不会遇到命名空间问题。
  • 实施可能足够小,以至于额外的膨胀不会成为问题。
  • 这两个实现都来自相同的String,所以你不应该有同样的问题。

你会有这样的事情:

# 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

这是一大堆kludge所以我不推荐它。有可能这种方法有意义。

正确的方法是从你的C调用is_number作为Ruby方法。调用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&amp; R模式结束。你还应该调整你的编译器的警告标志,以便它大声抱怨这类事情。