使用C++和Lua Api,通过工厂函数进行就地分配。

我在我的C++代码和一些Lua脚本中使用的类。类的相关部分看起来像:

typedef boost::shared_ptr<Thing> ThingPtr; // 方便

class Thing
{
public:
    Thing() { /* 做一些事情 */ }
    虚拟 ~Thing() { }

    ThingPtr createThing()
    {
        ThingPtr thing(new Thing);

        // 初始化不能在构造函数中完成
        thing->doSomeInit();

        返回物品;
    }

// 其他东西....

};

我在Lua中暴露了这个类(不使用绑定或任何“高级”的东西)。在我添加工厂函数之前,创建_Thing_的Lua函数如下:

int MyLua::newThing(lua_State* L)
{
    int size = sizeof(Thing);

    // 在此处分配新的Thing对象
    new ((Thing*)lua_newuserdata(L, size)) Thing();

    luaL_setmetatable(L, "MyStuff.thing");

    return 1;
}

一旦我添加了工厂函数,我会像这样做:

int MyLua::newThing(lua_State* L)
{
    int size = sizeof(Thing);

    // 在此处分配新的Thing对象
    Thing* thing = new ((Thing*)lua_newuserdata(L, size)) Thing();
    thing->doSomeInit();

    luaL_setmetatable(L, "MyStuff.thing");

    return 1;
}

这很好,好像没有什么问题,但是现在我想使_Thing_的构造函数私有,以便在C++代码的其他地方强制使用工厂函数。所以,现在我有这样的东西:

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的调用使我感到不舒服。

点赞
用户2491746
用户2491746

这可能会让你感到不舒服; memcpy 仅允许用于微不足道的可复制类型 (Thing 不是这样的类型)。我甚至不确定 new (lua_newuserdata(L, size)) Thing() 是否被允许,因为 Lua 默认使用 realloc 来申请新的内存,这可能导致内存被移动 (即 realloc 可能仍然会 memcpy 它)。

我认为解决方案是动态分配你的 Thing (似乎你的 createThing 工厂确实使用了智能指针),并在用户数据中存储一个 C 指针指向该对象,以及一个 __gc 元方法来清理你的对象。对于智能指针,这更加复杂,但它涉及将智能指针的副本存储在堆上,在用户数据中存储一个 C 指针指向智能指针,并在 __gc 元方法中释放智能指针。

2013-10-02 10:32:53
用户577603
用户577603

你无法将C++对象所有权转移给Lua。

你原来的代码存在缺陷,因为它永远不会调用你的Thing的析构函数。虽然Lua将通过lua_newuserdata分配的内存进行垃圾回收,但它不会调用对象的析构函数(因为Lua作为C库,不知道析构函数的概念)。

因此,你需要在C++端上创建一个独立的构造函数来管理你的对象的生命周期,只将裸指针(不拥有)传递给Lua作为userdata。

2013-10-02 10:45:04