存储和返回Lua用户数据
2015-9-23 18:27:35
收藏:0
阅读:62
评论:1
我在 C++ 中有以下类:
class B;
class A {
B* GetB();
void SetB(B*& b) { _b = b;};
private:
B* _b;
}
以下是部分 lua 绑定代码:
int A::setB(lua_State* L) {
A* a = checkA(L,1) // Macro for luaL_checkudata
B* b = checkB(L,2) // again similar macro
a->SetB(b);
return 0;
}
int A::getB(lua_State* L) {
A* a = checkA(L,1) // Macro for luaL_checkudata
B* b = a->GetB();
// 我应该如何返回与此 B* 实例相关联的已创建的 userdata?
// 目前我这样做:
B** bp = (B**)lua_newuserdata(L, sizeof(B*));
*bp = b;
luaL_getmetatable(L, "B");
lua_setmettable(L, -2);
return 1;
}
我想将它们作为 Lua userdata 进行包装,这样我就可以做些类似于本文中的操作:
local a = a.new() -- create new userdata
local b = b.new() -- create new userdata
a:SetB(b) -- associate B with A
local b2 = a:GetB() -- get the B associated with A back, and stored as b2
当我打印 b 和 b2 的地址时,我得到两个唯一的地址,这很有意义,因为我已经调用了 lua_newuserdata。但理想情况下,我希望它返回相同的 userdata,因为它们指向相同的内存块。如何做到这一点?
点赞
评论区的留言会收到邮件通知哦~
推荐文章
- 如何将两个不同的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中获取用户配置主目录的跨平台方法
你可以通过使用一个用户值表在 Lua 中镜像 C++ 对象的成员变量,这样你的方法就变成了:
int A::setB(lua_State* L) { A* a = checkA(L,1) // luaL_checkudata 的宏 B* b = checkB(L,2) // 相似的宏 a->SetB(b); // 如果你对一致性很谨慎,应该预分配 uservalue 表格槽的 "_b", // 这样以下代码在内存分配期间就无法失败 lua_getuservalue(L, 1); // 在 Lua 5.1 中,使用 lua_getfenv lua_pushvalue(L, 2); // 在堆栈顶部复制 B userdata lua_setfield(L, -2, "_b"); // 将其存储在 uservalue 表中 return 0; } int A::getB(lua_State* L) { A* a = checkA(L,1) // luaL_checkudata 的宏 lua_getuservalue(L, 1); lua_getfield(L, -1, "_b"); return 1; }这样做的额外好处是,在 A 对象引用 B userdata 时,B userdata 不会被垃圾回收(导致悬空指针)。
确保每个 A 对象都 真正 拥有一个 uservalue 表,方法是在每个
lua_newuserdata()后使用lua_setuservalue()(对于 Lua 5.1,使用lua_setfenv())。你也可以添加检查来确保 C++ 前端和 Lua 前端仍然保持同步(如果你仍然从 C++ 使用接口),但如果你发现一个 B 对象没有镜像到 uservalue 表中,除了提出一个错误,你别无选择,因为你不知道它是如何到达那里和谁拥有它。
编辑:
如果你想管理一个 B 指针容器,你可以通过将所有 userdata 存储在一个表中(uservalue 表本身或其中的一个字段)并镜像所有容器操作来实现,但这很容易出错。作为替代方案,你可以将 uservalue 表用作从 C++ 指针值(轻量级 userdata)到 Lua 对象(全 userdata)的映射。这个映射将保持 B userdata 存活,你只需要通过指针值查找它们就可以了。你只需要在添加或删除 B 对象时更新映射。例如
int A::pushB(lua_State* L) { A* a = checkA(L,1) B* b = checkB(L,2) lua_getuservalue(L, 1); lua_pushlightuserdata(L, (void*)b); lua_pushvalue(L, 2); lua_rawset(L, -3); a->PushB(b); return 0; } int A::popB(lua_State* L) { A* a = checkA(L,1) B* b = a->PopB(); lua_getuservalue(L, 1); lua_pushlightuserdata(L, (void*)b); lua_rawget(L, -2); // 推入完整的 userdata if (!a->ContainsB(b)) { // 仅在此 b 不再在容器中时删除引用 // 如果做错了,b 可能会过早地被回收 //(当 A 仍然有一个存储指针时),或者太晚 //(在对象 a 被收集时) lua_pushlightuserdata(L, (void*)b); lua_pushnil(L); lua_rawset(L, -4); } return 1; }