C++ & Lua Api,具有工厂功能的就地分配



我有一个类,我在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 元方法中释放智能指针。

最新更新