将已经存在的C++对象从C++返回给Lua
2013-2-9 14:33:56
收藏:0
阅读:142
评论:1
在 C++ 中,假设我有一个创建类似二叉树结构的类,并且我像这样使用它:
CTreeRoot* root = new CTreeRoot(/*随便什么*/);
CNode* leftNode = root->getLeftNode();
CNode* rightNode = root->getRightNOde();
leftNode->doSomething();
rightNode->doSomething();
// 等等
并且假设左右节点都有自己的左右节点(因此是二叉树)。现在我想将其暴露给 Lua(不使用 luabind),以便我可以执行类似的操作:
local root = treeroot.new(/*随便什么*/);
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);
// 在 Lua 为我们分配的内存处创建 CNode 对象的新副本
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)
{
// “错误的”代码,但显示我希望能做的事情
lua_pushlightuserdata(L, (void*)leftNode);
lua_setmetatable(L, "MyLua.treenode");
}
else
{
lua_pushnil(L);
}
return 1;
}
请问有谁能告诉我如何使我的 MyLua::TreeRootGetLeftNode 方法将已经存在的对象的副本返回给 Lua,以便我可以在 Lua 中将该对象用作“对象”?
点赞
评论区的留言会收到邮件通知哦~
推荐文章
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
这里可以执行两个级别的内存优化。在您的第一个功能齐备但效率低下的解决方案中,当用户调用
getLeftNode()时,它必须创建一个CNode的副本以将其存储在 Lua userdata 中。此外,每当用户反复在相同的树上调用getLeftNode()时,它都会继续创建新的userdata来表示CNode,即使它以前已经创建过。在第一级优化中,您可以对此调用进行备忘录,以便每次用户请求相同的子树时,您可以简单地返回最初创建的userdata,而不是复制并构造另一个userdata来表示同一物。不过,这取决于要修改 Lua 接口、修改 C++ 实现还是忍耐一下,有3种方法可供选择。
MyLua.treenodeuserdata 目前包含了一个CNode对象的实际数据。然而,这是不幸的,因为这意味着每当您创建CNode对象时,您必须使用就地 new 将其存储到 Lua 立即在创建时分配的内存中。更好的做法可能是在MyLua.treenode的 userdata 中仅存储一个指针(CNode*)。这确实需要您修改MyLua.treenode的 Lua 接口,以便它现在将其数据视为指向CNode对象的指针。如果您希望直接在
MyLua.treenode的 userdata 中存储CNode数据,则必须确保在创建CTreeRoot时,每次都使用就地 new 从 Lua 分配的内存中构造CNode(或者您可以使用 C++ 标准库中使用的 分配器模式?)。然而,这并不那么优雅,因为您的CNode实现现在取决于 Lua 运行时,我不建议这样做。如果以上两种解决方案都不适用,则每当返回子节点时都必须进行复制,但是您仍然可以通过跟踪是否创建了相同的userdata(例如使用 Lua 表)来提高_在相同节点上重复调用_的效率。
在第二个级别的优化中,您可以通过使复制的子树成为原始树的弱引用来进一步节省内存。这样,每当您复制子树时,您只是创建对原始树的一部分的指针。或者,如果您希望子树在原始树被销毁后仍然存在,则可以使用强引用,但是那么您必须了解引用计数的细节。无论哪种情况,此优化纯粹在 C++ 级别上,与 Lua 接口无关,并且从您的代码中判断,我认为您已经在使用弱引用(
CNode*)了。注意:最好避免使用轻量级 userdata,除非用于内部实现。原因是轻量级 userdata 基本上等同于 C 指针,因此可以指向几乎任何东西。如果将轻量级 userdata 暴露给 Lua 代码,您将不知道轻量级 userdata 可能来自哪里或其中包含何种类型的数据,使用它会产生安全风险(以及可能导致段错误)。使用轻量级用户数据的_适当_方法是将其用作存储在 Lua 注册表中的 Lua 查找表的索引,该表可用于实现前面提到的备忘录。