用C++包装Lua的问题

我正在使用Lua C APIC++中,将其包装成以下类:

class LuaScripting {
public:
    lua_State *lua;

    LuaScripting();

    ~LuaScripting();

    bool execute_script(const std::string &script);
};

LuaScripting::LuaScripting() {
    lua = luaL_newstate(); /* 打开 Lua */
    luaL_openlibs(lua); /* 打开标准库 */

    /* 注册自定义函数 */
    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); /* 清理 Lua */
}

我通过以下方式进行测试:

int main() {
    // 禁用缓冲
    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)”后,写测试用例成功运行,但是“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 不起作用
    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; //错误:聚合‘lua_State lua’没有完成类型,因此无法定义

但是这样做不能编译。

通过lua_State **lua添加另一层间接性质可以解决“lua_close()”中的崩溃问题,但不能解决其他任何问题。

点赞
用户106104
用户106104

那是因为你正在复制LuaScripting对象。我打赌 testWritingInt8 声明如下:

void testWritingInt8(LuaScripting lua)

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

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

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

解决方案:通过删除复制构造函数和赋值运算符,确保 LuaScripting 对象不能被意外地复制。

// 在 LuaScripting 中
LuaScripting(const LuaScripting &) = delete;
LuaScripting &operator =(const LuaScripting &) = delete;

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

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


你的 math_sin 测试用例没有返回任何值到栈上,因为...你的脚本没有返回任何值。尝试使用 return math_sin(target_value) 而不是仅使用 math_sin(target_value)

2020-07-24 12:19:57