【问题标题】:Why does one work and one crash?为什么一个工作,一个崩溃?
【发布时间】:2013-02-23 11:48:07
【问题描述】:

这已经让我发疯了一个多星期。下面是游戏Bitfighter 中机器人玩家的两个 Lua 代码 sn-ps(用 C++ 编写,使用 LuaWrapper 的变体进行绑定)。

当我第一次启动 Lua 脚本时,两者都按预期工作。但经过几分钟激烈的对象创建和销毁后,变体 2 停止工作,并给我以下错误:

robot.lua:253: attempt to call missing or unknown method 'getEnergy' (a nil value)

在我看来,这些功能应该相同。谁能解释一下区别?

注意:目标是代表 C++ 对象的(重)用户数据。 getEnergy 和 getHealth 是正确注册的 C++ 函数。我可以轻松地重现这种不同的行为。这是 Lua 5.1,使用 luavec mod。

变体 1 - 始终有效

local mt = getmetatable(target)
local pow = mt.getEnergy(target) + mt.getHealth(target)

变体 2 - 在脚本运行任意时间后开始失败

 local pow = target:getEnergy() + target:getHealth()

【问题讨论】:

  • “任意时间”是多长时间?
  • 从 30 秒到 5 分钟不等。通常在 1 分钟左右。但它总是发生。

标签: c++ lua


【解决方案1】:

要跟踪停止工作时发生的情况,您可以将调用封装在 pcall 中,并使用 target 值探索发生了什么:

local ok, res = pcall(function() return target:getEnergy() + target:getHealth() end)
if not ok then
  local s = "Invalid target value: "..tostring(target).." "..type(target).."\n"
  for k, v in pairs(target) do s = s.."target "..tostring(k).." "..tostring(v).."\n" end
  for k, v in pairs(getmetatable(target)) do s = s.."meta "..tostring(k).." "..tostring(v).."\n" end
  -- add anything else that helps you figure out what happened to target
  error(res..s)
end
local pow = res

【讨论】:

  • 这并没有导致解决方案,但我接受了这个答案,因为它是我忽略的一种有用的调试技术。
【解决方案2】:

我强烈怀疑问题在于您有一个类似于此的类或结构:

struct Foo
{
    Bar bar;
    // Other fields follow
}

你已经通过 LuaWrapper 将 Foo 和 Bar 暴露给 Lua。这里重要的一点是barFoo 结构中的第一个字段。或者,您可能有一些从其他基类继承的类,并且派生类和基类都暴露给 LuaWrapper。

LuaWrapper 使用一个称为标识符的函数来唯一地跟踪每个对象(例如给定对象是否已经添加到 Lua 状态)。默认情况下,它使用对象地址作为键。在上述情况下,Foo 和 Bar 可能在内存中具有相同的地址,因此 LuaWrapper 可能会混淆。

这可能会导致在尝试查找方法时抓取错误对象的元表。显然,由于它正在查看错误的元表,它不会找到您想要的方法,因此看起来好像您的元表神秘地丢失了条目。

我签入了一项更改,该更改跟踪每个对象的每种类型的数据,而不是一大堆。如果您将您的 LuaWrapper 副本更新到存储库中的最新版本,我相当肯定您的问题将得到解决。

【讨论】:

  • 我认为您至少部分正确,我希望(真的,真的希望!)您最近对 LuaWrapper 的更新已经解决了根本问题,但它仍然留下了关于为什么其中之一的问题我的例子有效,另一个失败。我希望在您描述的情况下两者都会失败。
【解决方案3】:

当你说它在某个阶段停止工作,但之前工作正常......难道你在运行时的任何地方覆盖了 .getEnergy 函数? 也许您在某处运行foo.getEnergy = nil 而不是foo.getEnergy == nil?听起来可能是后期初始化出错了:)

【讨论】:

  • 那没有发生;证明是第一个变体继续有效。如果我覆盖 getEnergy,两者都会崩溃。而且,我尝试在每次调用之前转储元表,并确认该函数仍然存在。
猜你喜欢
  • 1970-01-01
  • 2012-06-13
  • 2013-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-23
  • 2012-11-21
相关资源
最近更新 更多