c++ - 将已存在的对象从 C++ 返回到 Lua

标签 c++ lua

在 C++ 中,假设我有一个创建二叉树结构的类,我使用它是这样的:

CTreeRoot* root = new CTreeRoot(/* whatever */);
CNode* leftNode = root->getLeftNode();
CNode* rightNode = root->getRightNOde();

leftNode->doSomething();
rightNode->doSomething();

// etc

并假设左右节点有自己的左右节点(因此,二叉树)。现在我想将它公开给 Lua(使用 luabind)这样我就可以做同样的事情:

local root = treeroot.new(/* whatever */)
local left = root:getLeftNode()
local right = root:getRightNode()

left:doSomething();
right:doSomething(); 

我已经完成了其中的大部分工作。但是,对于 getLeftNode() 和 getRightNode() 方法,我很确定我做的是“错误的”。以下是我在 C++ 中实现 getLeftNode() 的方式:

int MyLua::TreeRootGetLeftNode(luaState* L)
{
    CTreeRoot* root = (CTreeRoot*)luaL_checkudata(L, 1, "MyLua.treeroot");
    CNode* leftNode = root->getLeftNode();

    if (leftNode != NULL)
    {
        int size = sizeof(CNode);

        // create a new copy of the CNode object inplace of the memory Lua
        // allocated for us
        new ((CNode*)lua_newuserdata(L,size)) CNode((const CNode&)*leftNode);
        lua_setmetatable(L, "MyLua.treenode");
    }
    else
    {
        lua_pushnil(L);
    }

    return 1;
}

我将 userdata 转换回 CTreeRoot 对象,调用 getLeftNode(),确保它存在,然后(这里是“错误的部分”)我创建了另一个 userdata 数据对象,它带有一个复制构造函数来复制我想要返回的对象。

这是针对此类场景的“标准做法”吗?您似乎希望避免创建该对象的另一个拷贝,因为您真正想要的只是对已存在对象的引用。

听起来这将是 lightuserdata 的完美位置,因为我不想创建新对象,我很乐意返回已经存在的对象。但是,问题是 lightuserdata 没有元表,所以一旦我取回这些对象,它们对我来说就没有用了。基本上,我想做类似的事情:

int MyLua::TreeRootGetLeftNode(luaState* L)
{
    CTreeRoot* root = (CTreeRoot*)luaL_checkudata(L, 1, "MyLua.treeroot");
    CNode* leftNode = root->getLeftNode();

    if (leftNode != NULL)
    {
        // "WRONG" CODE BUT SHOWS WHAT I WISH I COULD DO
        lua_pushlightuserdata(L, (void*)leftNode);
        lua_setmetatable(L, "MyLua.treenode");
    }
    else
    {
        lua_pushnil(L);
    }

    return 1;
}

谁能告诉我如何让我的 MyLua::TreeRootGetLeftNode 方法返回给 Lua 一个已经存在的对象的拷贝,这样我就可以将该对象用作“对象” ' 在 Lua 中?

最佳答案

这里可以执行两个级别的内存优化。在您的第一个功能但效率低下的解决方案中,当用户调用 getLeftNode() 时,它必须创建 CNode 的拷贝以便将其存储在 Lua 用户数据中.此外,每次用户在同一棵树上重复调用 getLeftNode() 时,它将继续创建新的用户数据来表示 CNode,即使它之前已创建。

在第一级优化中,可以memoize这个调用,这样每次用户请求同一个子树时,都可以简单的返回原来创建的userdata,而不用复制构造另一个userdata 来表示相同的东西。不过,有 3 种方法可以解决这个问题,具体取决于您是想修改 Lua 接口(interface)、更改 C++ 实现,还是硬着头皮。

  • MyLua.treenode 用户数据当前包含一个CNode 对象的实际数据。然而,这是不幸的,因为这意味着每当你创建一个 CNode 对象时,你必须使用 placement new 将它存储到创建时立即由 Lua 分配的内存中。可能更好的方法是在 MyLua.treenode 的用户数据中简单地存储一个指针(CNode*)。这确实需要您修改 MyLua.treenode 的 Lua 接口(interface),以便它现在将其数据视为指向 CNode 对象的指针。

  • 如果您希望将 CNode 数据直接存储在 MyLua.treenode 用户数据中,那么您必须确保在创建您的 CTreeRoot,它会使用 placement new 每次从 Lua 分配的内存中构造 CNode(或者你可以使用 C++ 标准库中使用的 allocator pattern?)。然而,这不太优雅,因为您的 CNode 实现现在取决于 Lua 运行时,我不推荐这样做。

  • 如果以上两种方案都不合适,那么每次返回子节点都只需要复制一份,不过对于在同一节点上重复调用还是可以提高效率的 em> 通过跟踪您之前是否创建了相同的用户数据(例如,使用 Lua 表)。

在第二级优化中,您可以通过使复制的子树成为对原始树的片段的弱引用 来进一步节省内存。这样,无论何时复制子树,都只是创建一个指向原始树的一部分的指针。或者,如果您希望您的子树即使在原始树被销毁后仍然存在,您也可以使用强引用,但是您将不得不进入引用计数的血淋淋的细节。在任何一种情况下,这种优化都纯粹是在 C++ 级别上进行的,与 Lua 接口(interface)无关,从您的代码来看,我假设您已经在使用弱引用 (CNode*)。

注意:除非在内部实现中使用,否则最好避免使用轻型用户数据。原因是轻型用户数据本质上等同于 C 指针,因此可以指向任何东西。如果您将轻量级用户数据暴露给 Lua 代码,您将不知道轻量级用户数据可能来自哪里或它包含什么类型的数据,使用它会带来安全风险(以及程序段错误的可能性)。 适当使用轻型用户数据的方法是将其用作存储在 Lua 注册表中的 Lua 查找表的索引,它可用于实现前面提到的内存。

关于c++ - 将已存在的对象从 C++ 返回到 Lua,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14788319/

相关文章:

Lua:以不区分大小写的方式合并2个字符串集合

c++ - 视觉 C++ : creating a dll using libcurl

c++ - 如何在发行版本中包含声音片段

c++ - 类模板的模板参数推导陷阱

mysql - 从 Lua 调用存储过程返回不完整的结果

lua - 如何改进一长串 elseif 语句?

lua - GC-ed lua 对象的析构函数

c++ - T(n)嵌套循环

android - YUV (NV21) 到移动设备上的 BGR 转换( native 代码)

unicode - 如何在lua中编写unicode符号