我有一个类,我在C++代码和一些Lua脚本中使用。该类的相关部分如下所示:
typedef boost::shared_ptr<Thing> ThingPtr; // convenient
class Thing
{
public:
Thing() { /* do some stuff */ }
~virtual Thing() { }
ThingPtr createThing()
{
ThingPtr thing(new Thing);
// initialization can't be done in constructor
thing->doSomeInit();
return thing;
}
// other stuff....
};
我在Lua中公开了这个类(不使用绑定或任何"花哨"的东西)。在我添加工厂函数之前,我用于创建事物的Lua函数如下所示:
int MyLua::newThing(lua_State* L)
{
int size = sizeof(Thing);
// allocate a new Thing object in place
new ((Thing*)lua_newuserdata(L, size)) Thing();
luaL_setmetatable(L, "MyStuff.thing");
return 1;
}
添加工厂函数后,我做了如下操作:
int MyLua::newThing(lua_State* L)
{
int size = sizeof(Thing);
// allocate a new Thing object in place
Thing* thing = new ((Thing*)lua_newuserdata(L, size)) Thing();
thing->doSomeInit();
luaL_setmetatable(L, "MyStuff.thing");
return 1;
}
这似乎很好,除了现在我想将 Thing 的构造函数设为私有,以便在C++代码的其他地方强制使用 factory 函数。所以,现在我有这样的东西:
int MyLua::newThing(lua_State* L)
{
int size = sizeof(Thing);
ThingPtr thing = Thing::createThing();
void* space = lua_newuserdata(L, size);
memcpy(space, client.get(), size);
luaL_setmetatable(L, "MyStuff.thing");
return 1;
}
我的问题是:有没有更好的方法可以做到这一点?对memcpy的召唤让我感到不舒服。
您不能将C++对象的所有权转让给 Lua。
您的原始代码是有缺陷的,因为它永远不会为您的Thing
调用析构函数。虽然Lua会垃圾收集通过lua_newuserdata
分配的内存,但它不会调用对象的析构函数(仅仅是因为Lua作为一个C库,不知道析构函数的概念)。
因此,您将需要在C++端使用一个单独的构造来管理对象的生存期,并且仅将原始(非拥有)指针传递给Lua以作为用户数据公开。
它应该让你不舒服; memcpy
只允许用于平凡可复制的类型(Thing
不是这样的类型)。我什至不确定是否允许new (lua_newuserdata(L, size)) Thing()
因为 Lua 默认使用realloc
来声明新内存,这可能会导致内存被移动(即 无论如何,realloc
可能会memcpy
它)。
IMO 的解决方案是动态分配您的Thing
(似乎您的createThing
工厂确实这样做,但使用智能指针),并使用清理对象的__gc
元方法在用户数据中存储指向对象的 C 指针。使用智能指针,这更复杂,但它涉及在堆上存储智能指针的副本,在用户数据中存储指向智能指针的 C 指针,然后在 __gc
元方法中释放智能指针。