我的基于ruby的数据分析项目需要高性能。我计划创建一个内联C函数,它读取一段数据并更新一些全局变量,我可以在ruby中进行更多处理。
以下玩具代码:
require 'inline'
class Foo
inline :C do |builder|
builder.c_raw_singleton <<SRC
static char *cstr = "hi you'all, welcome";
void write_global(VALUE self, VALUE *name){
rb_gv_set(rb_string_value_cstr(name), rb_str_new_cstr(cstr));
cstr[1] ++;
}
SRC
end
end
$bar = ""
Foo.write_global('bar') #=> 1
puts $bar;
Foo.write_global('bar') #=> 1
puts $bar;
会导致ruby解释器出现异常。
我想知道问题是什么。
更新1
感谢@ mu-is-too-short指出常量字符串无法修改,我将其更改为缓冲区(请参阅下文),但是出现了编译错误,如
WARNING: '&cstr[0]' not understood
WARNING: '"%s"' not understood
WARNING: '"hi you'all' not understood
WARNING: 'welcome"' not understood
WARNING: Can't find signature in "\t static char cstr[20];static int f(&cstr[0], \"%s\", \"hi you'all, welcome\");\n void write_global(VALUE self, VALUE *name){\n rb_gv_set(rb_string_value_cstr(name), rb_str_new_cstr(cstr));\n\t\tcstr[1] ++;\n }\n"
inline1a.rb:5:37: error: expected declaration specifiers or ‘...’ before ‘&’ token
builder.c_raw_singleton <<SRC
以下是不尝试修改常量字符串的新代码。
require 'inline'
class Foo
inline :C do |builder|
builder.c_raw_singleton <<SRC
static char cstr[20];
sprintf(&cstr[0], "%s", "hi you'all, welcome");
void write_global(VALUE self, VALUE *name){
rb_gv_set(rb_string_value_cstr(name), rb_str_new_cstr(cstr));
cstr[1] ++;
}
SRC
end
end
$bar = ""
Foo.write_global('bar') #=> 1
puts $bar;
Foo.write_global('bar') #=> 1
puts $bar;
答案 0 :(得分:2)
您的问题是您正在尝试修改只读内存。如果你把所有杂乱的东西都弄清楚了,你可以用一个小的C程序来展示它:
int
main(int argc, char **argv) {
char *cstr = "hi you'all, welcome";
cstr[1]++;
return 0;
}
如果你编译并运行它,你应该得到一个总线错误(或类似的),因为cstr
在只读内存中,cstr[1]++
正在尝试修改该只读内存。 Is it possible to modify a string of char in C?标签中有c以及可能数百个其他问题对此进行了一些讨论。
解决方案是不修改cstr
。我没有足够的细节来提供更具体的建议。
答案 1 :(得分:1)
注意RubyInline如何定义Ruby方法。如果您使用c_raw
或c_raw_singleton
,则会在-1
或rb_define_method
,so the method is called as的调用中使用rb_define_singleton_method
作为arity:
VALUE func(int argc, VALUE *argv, VALUE obj)
因此,在您的情况下,您的签名应该是:
VALUE write_global(int argc, VALUE *argv, VALUE self)
这是您的班级版本,并带有相应的变化。我还将cstr
声明移到前缀中以避免出现警告,并使用StringValueCStr
代替rb_string_value_cstr
,因为它是better documented,但我省略了任何错误检查(你应该添加,否则你可能会得到进一步的段错误):
class Foo
inline :C do |builder|
builder.prefix("static char cstr[] = \"hi you'all, welcome\";")
builder.c_raw_singleton <<SRC
VALUE write_global(int argc, VALUE* argv, VALUE self){
rb_gv_set(StringValueCStr(argv[0]), rb_str_new_cstr(cstr));
cstr[1] ++;
return Qnil;
}
SRC
end
end
使用此类,代码的输出为:
hi you'all, welcome
hj you'all, welcome