内联代码导致ruby解释器崩溃

时间:2017-10-18 02:40:59

标签: c ruby inline

我的基于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;

2 个答案:

答案 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?标签中有以及可能数百个其他问题对此进行了一些讨论。

解决方案是不修改cstr。我没有足够的细节来提供更具体的建议。

答案 1 :(得分:1)

注意RubyInline如何定义Ruby方法。如果您使用c_rawc_raw_singleton,则会在-1rb_define_methodso 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