在为 Ruby 编写 C++ 扩展时,我一直在努力解决的一个问题是,即使用户做了一些愚蠢的事情,也让它变得非常安全。那么他应该得到异常,但绝不会出现SegFault。一个具体的问题如下:我的 C++ 类有一个重要的构造函数。然后我使用 Rice API 来包装我的 C++ 类。如果用户在他的 Ruby 代码中重新定义了 initialize(),那么由 Rice 创建的 initialize() 函数将被覆盖,并且对象既不会被分配也不会被初始化。一个玩具示例可能如下:
class Person {
public:
Person(const string& name): m_name (name) {}
const string& name() const { return m_name; }
private:
string m_name;
}
然后我像这样创建 Ruby 类:
define_class<Person>("Person")
.define_constructor(Constructor<Person, const string&>(), Arg("name"))
.define_method("name", &Person::name);
然后以下 Ruby 代码会导致 Segfault
require 'MyExtension'
class Person
def initialize
end
end
p = Person.new
puts p.name
有两种可能性我会很高兴:禁止以某种方式覆盖 Ruby 中的初始化函数或检查 C++,如果对象已正确分配,否则抛出异常。
我曾经直接使用过 Ruby C API,然后就很简单了。我刚刚分配了一个由空指针和在 allocate() 函数中设置为 false 的标志组成的虚拟对象,在初始化方法中,我分配了真实对象并将标志设置为 true。在每个方法中,我检查了那个标志并引发了一个异常,如果它是假的。然而,我用 Ruby C API 写了很多愚蠢的重复代码,我首先必须包装我的 C++ 类,以便它们可以从 C 访问,然后包装和解包 Ruby 类型等,另外我必须检查这个愚蠢的标志每一种方法,所以我迁移到了赖斯,这真的很好,我很高兴。
然而,在 Rice 中,程序员只能提供一个构造函数,该构造函数在 rice 创建的 initialize() 函数中调用,而 allocate() 函数是预定义的,什么也不做。我认为没有一种简单的方法可以改变这一点或以“官方”方式提供自己的分配功能。当然,我仍然可以使用 C API 来定义 allocate 函数,所以我尝试以某种方式将 C API 和 Rice 混合,但后来我变得非常讨厌,我得到了奇怪的 SegFaults 并且真的很丑,所以我放弃了这个想法.
这里有没有人有过大米的经验,或者有没有人知道如何保证这个安全?