我有两个用于Ruby的C扩展(用于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
?
答案 0 :(得分:1)
您的函数在单独的编译单元中声明为static
,因此当然rb_ary_some_fun
无法看到rb_obj_is_number
。立即想到两个选项:
一种选择是将rb_obj_is_number
的C实现放在两个builder.c
调用都可访问的String中。这将为您提供该函数的两个副本,但是:
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
这是一大堆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模式结束。你还应该调整你的编译器的警告标志,以便它大声抱怨这类事情。