Lua,C++和消失的元表
背景
我和 Watusimoto 合作开发游戏 Bitfighter。我们使用一种名为 LuaWrapper 的变体来将我们的 C++ 对象与游戏中的 Lua 对象连接起来。我们还使用一种名为 lua-vec 的变体来加快向量运算速度。
我们一直在努力解决一个已经使我们困扰了一段时间的 bug。会出现随机崩溃,表明出现了损坏的元表。请参见 Watusimoto 在此问题上的发布。我不确定是否是由于元表损坏而导致的,但我已经看到了一些非常奇怪的行为,想在这里问问。
问题表现
例如,我们创建一个对象并将其添加到一个级别中,像这样:
t = TextItem.new()
t:setText("hello")
levelgen:addItem(t)
但游戏有时会(不总是)崩溃。出现一个错误:
attempt to call missing or unknown method 'addItem' (a nil value)
根据 Watusimoto 上面提到的答案建议,我已经将最后一行改为以下内容:
local ok, res = pcall(function() levelgen:addItem(t) end)
if not ok then
local s = "Invalid levelgen value: "..tostring(levelgen).." "..type(levelgen).."\n"
for k, v in pairs(getmetatable(levelgen)) do
s = s.."meta "..tostring(k).." "..tostring(v).."\n"
end
error(res..s)
end
如果在调用 levelgen 中的方法时出现问题,这将打印出 levelgen 的元表。
然而,有些事情很疯狂,当它失败并打印出元表时,元表恰好是正确的(具有正确的 addItem 调用和其他所有内容)。如果我在脚本加载时打印 levelgen 的元表,以及在使用上面的 pcall 时失败时,它们都是相同的,每个调用和 userdata 指针都是相同的,就像应该的那样。
好像 levelgen 的元表会在随机情况下自动消失一样。
有人知道发生了什么吗?
谢谢
注意: 这不仅发生在 levelgen 对象上。例如,它也发生在上面提到的 TestItem 对象上。事实上,相同的代码在我的电脑上在 levelgen:addItem(t) 行崩溃,在另一位开发人员的电脑上则在 t:setText("hello") 行崩溃,出现相同的错误消息 missing or unknown method 'setText' (a nil value)。
我强烈怀疑问题在于您有一个类或结构类似于这样:
struct Foo
{
Bar bar;
// Other fields follow
}
而且您已经通过 LuaWrapper 将 Foo 和 Bar 都暴露给了 Lua。这里重要的是 bar 是您的 Foo 结构体的第一个字段。或者,您可能有一些继承自其他基类的类,派生类和基类都暴露给 LuaWrapper。
LuaWrapper 使用称为标识符的函数来唯一跟踪每个对象(例如给定对象是否已经添加到 Lua 状态中)。默认情况下,它使用对象地址作为键。在上述情况下,可能会出现 Foo 和 Bar 在内存中具有相同的地址,因此 LuaWrapper 可能会混淆。
这可能会导致在尝试查找方法时抓取错误对象的元表。显然,由于它正在查看错误的元表,所以不会找到您想要的方法,因此它将显示为您的元表已神秘地丢失条目。
我已经检查了一下并进行了更改,使用每个类型的数据单独跟踪每个对象,而不是在一个巨大的堆叠中跟踪每个对象。如果您更新您的 LuaWrapper 副本,并从存储库中获取最新版本,我相当确定您的问题将得到解决。
在与上游合并(提交3c54015)LuaWrapper之后,这个问题已经消失了。这似乎是LuaWrapper中的一个bug。
感谢Alex!
- 如何将两个不同的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 处理的相同步骤,并尝试检测从您的期望中分岔的路径:
getmetatable(levelgen).__index返回什么?如果它是一个表,请检查其内容中是否有addItem。如果它是一个函数,则尝试使用(table,"addItem")调用它,并查看它返回什么。检查
getmetatable是否在调用之前和之后(或失败时)返回对同一对象的引用。调用经过几层元表间接引用吗?如果是这样,请尝试使用显式调用跟随相同的路径,并查看不同之处在哪里。
您是否使用了可能导致值在没有其他引用的情况下消失的“弱”键?
当检测到失败并继续查看它是否“找到”此方法的默认值时,您可以提供什么“默认”值?或者,当它被打破时,对于之后的每个调用都是损坏的?
如果保存 addItem 的正确值并在检测到它破损时“修复”它会怎样?
如果您仅处理错误(就像您所做的那样)并调用它 10 次,会显示有效结果至少一次(在失败后)吗?100 次?如果在它能正常工作时一直调用相同的方法,那么它会失败吗?这可以帮助您得出更具可重复性的错误。
我不熟悉 LuaWrapper,无法提供更具体的问题,但如果我是您,我会采取这些步骤。