对于一个学校项目,我正在使用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
。想到三个解决方案:
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上。 :)