我在C++
中使用Lua C API
,并将其包装成一个类,如下所示:
class LuaScripting {
public:
lua_State *lua;
LuaScripting();
~LuaScripting();
bool execute_script(const std::string &script);
};
LuaScripting::LuaScripting() {
lua = luaL_newstate(); /* Opens Lua */
luaL_openlibs(lua); /* Opens the standard libraries */
/* Register our custom function(s) */
lua_register(lua, "write8", lua_write8);
lua_register(lua, "write16", lua_write16);
lua_register(lua, "write32", lua_write32);
lua_register(lua, "read8", lua_read8);
lua_register(lua, "read16", lua_read16);
lua_register(lua, "read32", lua_read32);
lua_register(lua, "math_sin", math_sin);
}
LuaScripting::~LuaScripting() {
lua_close(lua); /* Clean up lua */
}
我测试如下:
int main() {
// Disable buffering
setbuf(stdout, nullptr);
LuaScripting lua_scripting;
testWritingInt8(lua_scripting);
testWritingInt16(lua_scripting);
testWritingInt32(lua_scripting);
testReadingInt32(lua_scripting);
test_math_sin(lua_scripting);
return EXIT_SUCCESS;
}
我遇到的问题:
- 在第一个
testWritingInt8()
之后调用类析构函数,它将运行lua_close(lua)
,即使类实例还没有超出范围。我没有使用任何线程。为什么会发生这种情况 - 调用
lua_close(lua)
时程序崩溃,原因是什么 - 在注释掉
lua_close(lua)
之后,写测试用例成功运行,但是readXX()
或math_sin()
返回空堆栈,尽管将值推送到堆栈上。为什么
实现:
static int math_sin(lua_State *lua) {
const auto value = luaL_checknumber(lua, 1);
const auto sine_result = sin(value);
lua_pushnumber(lua, sine_result);
return 1;
}
测试用例:
void test_math_sin(LuaScripting &lua_scripting) {
std::stringstream lua_script_builder;
const auto target_value = 90.f;
lua_script_builder << "math_sin(" << target_value << ")";
const auto script_result = lua_scripting.execute_script(lua_script_builder.str());
assert(script_result == LUA_OK);
// TODO Not working
const auto read_value = (int32_t) lua_tonumber(*lua_scripting.lua, -1);
assert(target_value == read_value);
}
我总是使用相同的lua_State *
,并且我只调用luaL_newstate()
一次。
作为一个尝试修复,我试图将lua状态声明为非指针:
lua_State lua; // error: aggregate ‘lua_State lua’ has incomplete type and cannot be defined
但这样做不会编译。
通过lua_State **lua
添加另一个间接级别可以修复lua_close()
的崩溃问题,但不会修复任何其他问题。
这是因为您正在复制LuaScripting
对象。我打赌testWritingInt8
是这样声明的:
void testWritingInt8(LuaScripting lua)
请注意,lua
参数不是指向LuaScripting对象的指针或引用,它是LuaScription对象。
因此,当您调用testWritingInt8(lua)
时,计算机会将LuaScripting对象复制到一个新对象中,调用该函数,并在调用结束时销毁该新对象。
现在,为什么会崩溃?好吧,LuaScripting
类没有复制构造函数(LuaScripting(const LuaScripting &)
(,所以编译器创建了一个默认构造函数,它只复制所有成员变量——在这种情况下,lua
指针被复制。然后,当新对象被破坏时,它释放Lua状态。
解决方案:通过删除复制构造函数,使LuaScripting
对象不会被意外复制。还有赋值运算符。
// inside LuaScripting
LuaScripting(const LuaScripting &) = delete;
LuaScripting &operator =(const LuaScripting &) = delete;
然后确保通过引用传递LuaScripting
值。
如果你想移动LuaScripting
对象,比如说,如果你想将它们存储在向量中,但仍然不复制它们,你可以定义move构造函数和move赋值运算符,这超出了这个答案的范围。
您的math_sin
测试用例不会在堆栈上返回任何值,因为。。。您的脚本不返回任何值。尝试使用return math_sin(target_value)
,而不是仅使用math_sin(target_value)
。