如何破坏和重新创建lua_State

时间:2011-11-27 00:57:20

标签: c++ lua

对于学校项目,我使用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脚本?

4 个答案:

答案 0 :(得分:6)

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

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

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

如果是这样,则此行是导致错误的原因:

lua = LuaInterface();

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

我想到了三种解决方案:

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

    luaVM.reset(lua_open(), lua_close);

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

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

lua = LuaInterface();

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

答案 1 :(得分:3)

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

你通常不应该只是与这样的构造函数和析构函数一起玩。只是做

lua = LuaInterface();
initLua();

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

答案 2 :(得分:3)

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

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

答案 3 :(得分:1)

(来自未来的问候!我从另一个Lua问题结束了这里,但我很震惊地看到这个问题得到了很好的解决。希望我能帮助未来的访问者解决这个问题。)

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

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

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

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

虽然我不建议使用展示位置析构函数和展示位置,但可以以这种方式实现您的赋值运算符。

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

    return *this;
}