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

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

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脚本?

最佳答案

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

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

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

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

lua = LuaInterface();

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

想到三个解决方案:
  • 通过声明私有(private)副本构造函数和赋值运算符,而不执行它们,使LuaInterface不可复制。 (这是相对常见的:http://www.artima.com/cppsource/bigtwo2.html)然后添加一个与析构函数相同的LuaInterface.reset()成员函数,其次是构造函数。您可以通过将共享部分移至私有(private)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上。 :)

    09-25 21:01