【问题标题】:luabind : Accessing an invalidated c++ object from lua results in access violationluabind : 从 lua 访问无效的 c++ 对象会导致访问冲突
【发布时间】:2013-01-25 22:16:07
【问题描述】:

luabind 是否可以检查对导出类(对象)的成员函数调用是否针对有效对象?

假设我有一个使用 luabind 暴露给 lua 的名为 Actor 的类。我从 C++ 调用一个 lua 函数,并以一个 actor 对象作为参数。现在,在函数完成之前,脚本编写会将参与者对象放入全局 lua 引用中,以便稍后访问。

稍后,演员对象从 C++ 站点中删除,另一个函数被调用,试图访问无效的演员对象(其中的任何方法) - 显然,由于它已被删除,它会导致崩溃(访问违规)

样本:

local myObjRef = nil

function doSomethingWithActor(actor)
 -- save, still valid object
 actor:Say("hello")
 myObjRef = actor
end

function calledAfterActorWasDeleted()
  --- will crash if the c++ object has been deleted meanwhile, works fine if it still exists
  myObjRef:Say("Crash...")
end

NIL 检查在这里没有帮助,这可以在 luabinds 网站上检查吗?这些函数使用 lua_pcall(....) 执行,堆栈跟踪显示 luabinds call.hpp results = maybe_yield(L, lua_gettop(L) - arguments, (Policies*)0); 处的错误;

如果没有,是否有其他解决方案如何确保编写脚本的人不会产生这些问题?

【问题讨论】:

    标签: c++ lua luabind


    【解决方案1】:

    现在在函数完成之前,脚本写入会将参与者对象放入全局 lua 引用中,以便稍后访问。

    这就是您的问题所在。如果您希望 Lua 代码拥有该对象(即保持该对象的存在),那么您需要使用 Luabind 机制告诉 Luabind 您想要这样做。否则,如果你传递一个指向某个 Lua 函数的指针,Luabind 将假定该函数不会试图获得它的所有权。

    如果您希望所有权在 Lua 和 Luabind 之间共享,那么您应该将您的对象包装在 boost::shared_ptr 中,并使用 Luabind 的智能指针机制来执行此操作。

    您也可以简单地更好地隔离脚本。如果您有一些对特定参与者进行操作的脚本,那么该脚本及其包含的任何函数都应该与对象一起被销毁(即:丢失对它的所有引用)。这需要在 C++ 端进行适当的编码纪律。它还要求您使用 Lua 环境正确封装脚本的每个实例,这样它们就不能通过全局变量偷偷摸摸。最后,您需要让 C++ 完全控制何时调用脚本以及何时不调用脚本。

    否则,所有权是您的脚本编写者必须了解并小心谨慎的事情。他们不能像对待任何旧的 Lua 值一样对待 C++ 参数。


    如果您无法进行严格的编程实践或不切实际,那么您只需不将实际的 C++ 对象传递给 Lua。相反,您需要向 Lua 传递一些代理对象,它是对原始对象的引用。 boost::weak_ptr 是这样一个对象的一个​​很好的例子(尽管你不会将它完全传递给 Lua)。代理会将调用转发到实际对象。如果对象已被删除,代理会检测到这一点并失败或不执行任何操作。

    【讨论】:

    • 我的脚本文件已经被沙箱化(每个文件,另一个沙箱,他们无法访问其他人的变量)。但是,shared_ptr 会解决这个问题吗?即使我从 C++ 站点删除该对象 - 这是否意味着该对象仍然存在?或者是否有可能告诉 lua - 如果我关于删除我的 c++ 对象,我的本地 myObjRef 为零?
    • @Steve:你不会删除shared_ptr中的东西;这就是重点。您只需删除shared_ptr;如果这是最后一个 shared_ptr 实例,那么它将被删除。
    • 谢谢,但是 shared_ptr 概念不适合我的实现 - 那么是否可以告诉 lua 删除/NIL 引用即将删除的演员对象的所有脚本变量?如果我使用 luabind,lua 会存储指向我的对象的指针吗?如果是这样,我可以简单地为我的脚本迭代当前 lua_state 环境中的所有表/属性并比较地址吗?
    • @Steve:不,当您删除它时,您不能神奇地修改碰巧引用 C++ 对象的每个 Lua 变量。你需要锻炼纪律;要么你不需要删除 Lua 可能仍在使用的东西,要么你的 Lua 脚本不需要将短期对象存储在长期存储中。这些是您唯一的选择。
    • 这听起来像是一个很好的解决方案,但是我从未使用过指针模板或使用过 boost(我只有它是因为 luabind 需要它)——你会这么好心提供一个示例代码吗?目前我正在使用 luabind::globals(luaState)[actorID] = actor; 将我的演员放在全局中。然后当我调用一个函数时,我使用 lua_getglobal(luaState, actorID); 提供特定的演员作为参数。将其放入堆栈,然后只需调用 lua_pcall() (我首先将我的函数推送到堆栈上)
    【解决方案2】:

    我通过以下方式解决了我的问题:

    当我要删除一个对象时,我会遍历 C++ 中的所有 lua 函数(我将它们放在一个列表中,它们每个都绑定到特定的 actor 对象)。然后我检查每个上值(全局/本地变量可访问函数) - 然后我将用户数据指针与我即将删除的对象进行比较 - 如果它们匹配(及其类)并且 NIL 上值。或者,我可以删除那个有问题的功能,因为它无论如何都不能正常工作了。

    所以下一次调用该函数时,我只是得到一个软 lua 错误“尝试访问 xxx 一个 nil 值......” - 不再有访问冲突。

    我知道人们会说“不要使用 lua_getupvalue/lua_setupvalue - 它们仅用于调试!” - 但实际上没有记录或口头上的副作用 - 在我的情况下,它非常安全且运行良好 - 也没有我无法删除的剩余代理对象的问题。

    【讨论】:

      猜你喜欢
      • 2010-12-29
      • 1970-01-01
      • 2020-10-12
      • 2010-11-05
      • 1970-01-01
      • 1970-01-01
      • 2012-05-22
      • 2015-08-29
      • 2012-01-12
      相关资源
      最近更新 更多