【问题标题】:Problems with wrapping Lua into a C++ class将 Lua 包装到 C++ 类中的问题
【发布时间】:2020-11-14 06:16:06
【问题描述】:

我在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() 的崩溃问题,但没有解决任何其他问题。

【问题讨论】:

    标签: c++ linux class lua destructor


    【解决方案1】:

    那是因为您正在复制 LuaScripting 对象。我敢打赌testWritingInt8 是这样声明的:

    void testWritingInt8(LuaScripting lua)
    

    请注意,lua 参数不是指针或对 LuaScripting 对象的引用,它 LuaScripting 对象。

    所以当您调用testWritingInt8(lua) 时,计算机会将 LuaScripting 对象复制到一个新对象中,调用该函数,并在调用结束时销毁新对象。

    现在,为什么会崩溃?好吧,您的 LuaScripting 类没有复制构造函数 (LuaScripting(const LuaScripting &amp;)),因此编译器创建了一个默认构造函数,它只复制所有成员变量——在这种情况下,lua 指针被复制。然后,当新对象被销毁时,它会释放 Lua 状态。

    解决方案:通过删除复制构造函数,使LuaScripting 对象不会被意外复制。还有赋值运算符。

    // inside LuaScripting
    LuaScripting(const LuaScripting &) = delete;
    LuaScripting &operator =(const LuaScripting &) = delete;
    

    然后确保通过引用传递LuaScripting 值。

    如果你希望能够移动 LuaScripting 周围的对象 - 比如说,如果你想将它们存储在一个向量中,但仍然不复制它们 - 你可以定义一个 move构造函数移动赋值运算符,这超出了这个答案的范围。


    您的math_sin 测试用例不会在堆栈上返回任何值,因为...您的脚本不会返回任何值。试试return math_sin(target_value) 而不是math_sin(target_value)

    【讨论】:

    • 谢谢,你是对的。但是,当我推送到 lua 堆栈时,在对 math_sin() 的调用返回后,更改仍然丢失。任何想法如何解决这个问题?
    • @BullyWiiPlaza 如果您对测试用例有疑问,也许您应该向人们展示测试用例的代码。
    • 我现在添加了测试用例。
    • @BullyWiiPlaza 我回答了。
    猜你喜欢
    • 2016-10-19
    • 2015-11-26
    • 2021-08-29
    • 2019-01-11
    • 2010-10-11
    • 1970-01-01
    • 2013-03-20
    • 1970-01-01
    • 2020-03-30
    相关资源
    最近更新 更多