omn*_*nse 16 c ruby ruby-c-extension
所以,最近我不幸地需要为Ruby做一个C扩展(因为性能).由于我是有了解的问题VALUE(和仍然),所以我看着Ruby源代码,并发现:typedef unsigned long VALUE;(链接到源代码,但你会发现,有它做了一些其他的"方式",但我认为它本质上是一个long; 如我错了请纠正我).因此,在进一步调查时,我发现了一篇有趣的博文,其中说:
"......在某些情况下,VALUE对象可能是数据,而不是指向数据."
是什么让我困惑的是,当我试图将一个字符串在Ruby传递到C,并使用RSTRING_PTR();上VALUE(通过在Ruby的C函数),并尝试"调试"它与strlen();它返回4. 总是 4.
示例代码:
VALUE test(VALUE inp) {
unsigned char* c = RSTRING_PTR(inp);
//return rb_str_new2(c); //this returns some random gibberish
return INT2FIX(strlen(c));
}
Run Code Online (Sandbox Code Playgroud)
此示例始终返回1作为字符串长度:
VALUE test(VALUE inp) {
unsigned char* c = (unsigned char*) inp;
//return rb_str_new2(c); // Always "\x03" in Ruby.
return INT2FIX(strlen(c));
}
Run Code Online (Sandbox Code Playgroud)
有时在ruby中我看到一个异常,说"无法将模块转换为字符串"(或者沿着这些行, 但是我正在弄乱代码,试图解决这个问题,我现在无法重现错误当我尝试时会发生错误StringValuePtr();[我有点不清楚这究竟是做什么的.文档说它将传递的参数更改为char*inp):
VALUE test(VALUE inp) {
StringValuePtr(inp);
return rb_str_new2((char*)inp); //Without the cast, I would get compiler warnings
}
Run Code Online (Sandbox Code Playgroud)
所以,有问题的Ruby代码是: MyMod::test("blahblablah")
编辑:修正了一些拼写错误并稍微更新了帖子.
VALUE imp举办?指向对象/值的指针?价值本身?PS:我对C的理解并不是最好的,但这是一项正在进行的工作; 另外,请阅读代码片段中的注释以获取一些其他说明(如果有帮助).
谢谢!
emb*_*oss 29
Ruby Strings vs. C字符串
让我们先从字符串开始.首先,试图找回在C柱之前,它是好习惯叫StringValue(obj)上你的VALUE第一次.这确保你最终会真正处理Ruby字符串,因为如果它不是一个字符串,那么它会通过调用该对象的to_str方法将其转换为一个字符串.因此,这会使事情变得更安全,并防止偶尔发生的段错误.
接下来需要注意的是Ruby字符串没有被\0终止,因为你的C代码会期望它们能像strlen预期的那样工作.Ruby的字符串带有它们的长度信息 - 这就是为什么除了RSTRING_PTR(str)还有RSTRING_LEN(str)宏来确定实际长度.
所以StringValuePtr现在做的是将非零终止返回char *给你 - 这对于你有一个单独长度但不是你想要的缓冲区来说非常好strlen.用StringValueCStr相反,它会修改字符串是零结尾所以这是在C是希望它是零结尾功能的使用是安全的.但是,尽可能避免这种情况,因为这种修改比检索不需要修改的非零终止字符串要差得多.令人惊讶的是,如果你密切注意这一点你实际上需要"真正的"C字符串很少.
self作为隐式VALUE参数
当前代码无法正常工作的另一个原因是Ruby调用的每个C函数都是self作为隐式传递的VALUE.
Ruby中没有参数(例如obj.doit)转换为
VALUE doit(VALUE self)
固定数量的参数(> 0,例如obj.doit(a,b))转换为
VALUE doit(VALUE self,VALUE a,VALUE b)
Ruby中的var args(例如obj.doit(a,b = nil))转换为
VALUE doit(int argc,VALUE*argv,VALUE self)
在Ruby中.所以你在你的例子中所做的不是 Ruby传递给你的字符串,而是实际上当前的值self,即调用该函数时接收器的对象.你的例子的正确定义是
static VALUE test(VALUE self, VALUE input)
Run Code Online (Sandbox Code Playgroud)
我static指出要在C扩展中遵循的另一条规则.如果您打算在多个源文件中共享它们,请仅将C函数设置为公共.由于您附加到Ruby类的函数几乎不会出现这种情况,因此您应该static默认将它们声明为只有在有充分理由这样做的情况下才公开它们.
什么是VALUE以及它来自哪里?
现在到了更难的部分.如果你深入研究Ruby内部,那么你将在gc.c中找到函数rb_objnew.在这里,您可以看到任何新创建的Ruby对象都是VALUE通过被称为的对象转换为一个freelist.它被定义为:
#define freelist objspace->heap.freelist
Run Code Online (Sandbox Code Playgroud)
您可以将其想象objspace为一个巨大的映射,用于存储代码中给定时间点当前处于活动状态的每个对象.这也是垃圾收集者履行职责heap的地方,特别是结构是新物体诞生的地方.堆的"空闲列表"再次被声明为一个RVALUE *.这是Ruby内置类型的C内部表示.一个RVALUE实际上是定义如下:
typedef struct RVALUE {
union {
struct {
VALUE flags; /* always 0 for freed obj */
struct RVALUE *next;
} free;
struct RBasic basic;
struct RObject object;
struct RClass klass;
struct RFloat flonum;
struct RString string;
struct RArray array;
struct RRegexp regexp;
struct RHash hash;
struct RData data;
struct RTypedData typeddata;
struct RStruct rstruct;
struct RBignum bignum;
struct RFile file;
struct RNode node;
struct RMatch match;
struct RRational rational;
struct RComplex complex;
} as;
#ifdef GC_DEBUG
const char *file;
int line;
#endif
} RVALUE;
Run Code Online (Sandbox Code Playgroud)
也就是说,基本上是Ruby知道的核心数据类型的联合.遗漏了什么?是的,那里nil不包含Fixnums,Symbols 和布尔值.这是因为这些类型的对象是使用直接代表的unsigned long是一个VALUE归结到底.我认为那里的设计决策(除了一个很酷的想法)解除引用指针的效果可能比转换为VALUE实际代表的当前所需的位移效果稍差.实质上
obj = (VALUE)freelist;
Run Code Online (Sandbox Code Playgroud)
说给我目前任何freelist积分,治疗就是unsigned long.这是安全的,因为freelist是一个指针RVALUE- 指针也可以安全地解释为unsigned long.这意味着VALUE除了那些携带Fixnums,symbol,nil或Booleans之外的每一个都基本上指向a RVALUE,其他的直接表示在VALUE.
你的上一个问题,你如何检查代表什么VALUE?您可以使用TYPE(x)宏来检查某个VALUE类型是否是"原始"类型之一.
VALUE test(VALUE inp)
Run Code Online (Sandbox Code Playgroud)
第一个问题是:inp是self(因此,在您的情况下,模块).如果你想引用第一个参数,你需要在它之前添加一个self参数(这使我添加-Wno-unused-parameters到我的cflags中,因为它从未在模块函数的情况下使用):
VALUE test(VALUE self, VALUE inp)
Run Code Online (Sandbox Code Playgroud)
您的第一个示例使用模块作为字符串,这当然不会产生任何好处.RSTRING_PTR缺乏类型检查,这是不使用它的好理由.
VALUE是对Ruby对象的引用,但不是直接指向它可能包含的内容(如字符串中的char*).您需要使用某些宏或函数来获取该指针,具体取决于每个对象.对于字符串,您希望StringValuePtr(或StringValueCStr确保字符串以空值终止)返回指针(它不会以任何方式更改 VALUE的内容).
strlen(StringValuePtr(thing));
RSTRING_LEN(thing); /* I assume strlen was just an example ;) */
Run Code Online (Sandbox Code Playgroud)
VALUE至少在MRI和YARV中,object_id物体的实际内容是物体的(或者至少是在移位后).
对于您自己的对象,VALUE很可能包含指向您可以使用的C对象的指针Data_Get_Struct:
my_type *thing = NULL;
Data_Get_Struct(rb_thing, my_type, thing);
Run Code Online (Sandbox Code Playgroud)