【问题标题】:How destroy and recreate a lua_State如何销毁和重新创建一个 lua_State
【发布时间】:2014-04-09 23:17:09
【问题描述】:

对于一个学校项目,我正在使用 Lua 向游戏引擎添加一些脚本功能。该引擎的加载速度很慢,因此我不必每次更改我的 Lua 脚本时都重新启动它,而是希望能够重新加载脚本。即使其他脚本作者以不可预知的方式污染了全局状态,我也希望能够以一种安全可靠的方式执行此操作。

在我看来,最简单的方法是简单地销毁我的 C++ 代码中的lua_State,然后创建一个新的,重新绑定我的函数并重新加载所有必要的脚本。但是,我在让它正常工作时遇到了一些麻烦。我有一个作为 Lua 虚拟机接口的类,它具有以下构造函数和析构函数:

LuaInterface::LuaInterface()
{
  luaVM = lua_open();
  luaL_openlibs(luaVM);

  // Create the ortsgfx table.
  lua_newtable(luaVM);
  lua_setglobal(luaVM, TABLE_NAME);
}

LuaInterface::~LuaInterface()
{
  // Close the Lua virtual machine
  lua_close(luaVM);
}

注意 lua_close(luaVM) 在对象的析构函数执行时被调用。我尝试使用以下代码从游戏引擎重置 Lua:

lua.~LuaInterface();
lua = LuaInterface();
initLua();

(当然,Lua 是一个 LuaInterface 对象。)当我尝试初始化我的一个表时,这会导致initLua() 出现分段错误。 但是,如果我在析构函数中删除 lua_close(luaVM) 调用,那么一切正常。

我做错了什么?此外,有没有更好的方法来重新加载我的 Lua 脚本?

【问题讨论】:

  • lua_close() 是否进行任何内存释放?

标签: c++ lua


【解决方案1】:

我有使用 C++ 的经验,但没有使用 Lua,所以这是我的猜测:

您的LuaInterface 类可能没有实现赋值运算符和复制构造函数,否则您可能会将它们与默认构造函数和析构函数一起发布。见三法则:

http://en.wikipedia.org/wiki/Rule_of_three_(C++_programming)

如果是这样,那么这行就是导致错误的原因:

lua = LuaInterface();

因为右边的LuaInterface()会构造一个临时实例,而自动生成的operator=会将lua_State复制到lua中。在此行之后,右侧未命名的临时对象被销毁并释放 lua 也认为拥有的同一 lua_State

想到三个解决方案:

  • 通过声明私有复制构造函数和赋值运算符来使LuaInterface 不可复制,并且从不实现它们。 (这个比较常见:http://www.artima.com/cppsource/bigtwo2.html)然后添加一个LuaInterface.reset()成员函数,它的作用和析构函数一样,后面跟着构造函数就行了。您可以通过将共享部分移动到私有 init() 函数来保持代码干净。 (注意在此函数中使用 C++ 异常,使对象处于无效状态。)
  • 使LuaInterface 不可复制,但不要使用LuaInterface lua 变量,而是使用指针或boost::optional<LuaInterface> lua。这样,您无需手动调用析构函数即可销毁和重新创建变量。
  • 使用新的std::shared_ptr 使LuaInterface 成为共享引用类。您只需使用 std::shared_ptr<LuaState> luaVM 成员变量而不是原始指针,并在您调用的构造函数中:

    luaVM.reset(lua_open(), lua_close);

    在这种情况下,您甚至不需要析构函数,但您应该阅读shared_ptr 以及自动生成的成员现在做了什么。

使用这三种解决方案中的任何一种,您都可以使用简单的赋值来重新创建状态:

lua = LuaInterface();

现在我希望问题出在 C++ 上,而不是 Lua 上。 :)

【讨论】:

    【解决方案2】:

    我没有看到initLua 的来源,所以我只能猜测,但是...

    你通常不应该像这样玩弄构造函数和析构函数。做吧

    lua = LuaInterface();
    initLua();
    

    该类应该有一个赋值运算符来处理它拥有的lua_States。

    【讨论】:

      【解决方案3】:
      lua.~LuaInterface(); 
      lua = LuaInterface(); 
      initLua();
      

      这是未定义的行为。你应该做简单的lua = LuaInterface();。您还有其他问题,不遵守三法则,但这也是 UB。

      【讨论】:

      • 直接调用析构函数后唯一明智的做法是用placement new重新初始化变量或释放变量占用的内存,但即使这样在这里也没有意义,因为直接调用析构函数只有在使用placement new 初始化变量后才有意义...
      【解决方案4】:

      (来自未来的问候!我从另一个 Lua 问题来到这里,但看到这个问题处理得如此糟糕,我感到很震惊。希望我可以帮助未来的访客解决这个问题。)

      如果您要进行就地销毁,则需要跟进就地构造。你的方法是错误的。

       lua.~LuaInterface();
       new (&lua) LuaInterface();
      

      从那里,我建议您熟悉 C++11 移动语义。然后,您可以使用漂亮的语法使该类工作。

      lua = LuaInterface();
      // Destructs old instance. Constructs new one. Moves into your variable.
      

      虽然我不建议使用placement destructor 和placement new,但您可以以这种方式实现您的赋值运算符。

      LuaInterface& LuaInterface::operator=(LuaInterface&& other)
      {
          if (this != &other)
          {
              this->~LuaInterface();
              new (this) LuaInterface(other);
          }
      
          return *this;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-21
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多