美文网首页Lua游戏开发中的 C/C++ Lua
C++ 编译 Lua 从而使 Lua 利用 C++ 异常

C++ 编译 Lua 从而使 Lua 利用 C++ 异常

作者: YLME | 来源:发表于2019-08-18 11:50 被阅读0次

    Lua 源码文件 ldo.c 中有如下说明:

    LUAI_THROW/LUAI_TRY define how Lua does exception handling. By default, Lua handles errors with exceptions when compiling as C++ code

    搜索使用 C++ 编译 Lua 时,也有提到使用 C++ 异常: According to the Lua Wiki, if Lua 5.1 or later is compiled as C++, it will use C++ exceptions.

    阅读这篇博客时,云大也提到:

    看 ldo.c 前面的 LUAI_THROW LUAI_TRY 等宏就是做的这个事情。所以,如果你用 C++ 做宿主语言,就应该用 C++ 编译器编译 Lua 库。

    并且在下面一段中的一句话给出了如果用 C++ 做宿主语言,应该用 C++ 编译 Lua 的原因:

    Lua 在内部发生异常时,VM 会在 C 的 stack frame 上直接跳至之前设置的恢复点,然后 unwind lua vm 层次上的 lua stack 。lua stack (CallInfo 结构)在捕获异常后是正确的,但 C 的 stack frame 的处理未必如你的宿主程序所愿。也就是 RAII 机制很可能没有被触发。

    上面这段话的意义在于在 Lua 中使用 C++ 异常,如果 Lua 抛出异常,此时走 C++ 异常机制,可确保对象被正确销毁,即析构函数被调用,也就保证了 RAII 机制。

    举个例子,如下代码片段,在创建 C++ 对象 t 后发生了异常,此时使用 C++ 编译的 Lua 库,可保证 t 的析构函数会被调用。

    static int
    test_func(lua_State *L) {
        Test t;
        luaL_error("there is an error here"); // 模拟 Lua C API 抛出异常
    }
    

    下面测试使用 C 和 C++ 编译的 Lua 库,来验证对象的析构函数是否会被调用。

    • 编译 Lua 。

    编译 C 版本 Lua 库:make linux 。库名为 liblua.a 。
    编译 C++ 版本 Lua 库:make linux CC="g++" 。修改库名为 libluacc.a 。

    • 编写 C++ 宿主程序,并编译。
    #include <iostream>
    #include <string>
    
    #ifndef LUA_CLIB
    #include <lua/lua.h>
    #include <lua/lualib.h>
    #include <lua/lauxlib.h>
    #else
    #include <lua/lua.hpp>
    #endif
    
    using namespace std;
    
    class Test {
    public:
        Test()
        {
            cout << "Test ctor:" << this << endl;
        }
    
        ~Test()
        {
            cout << "Test dctor:" << this <<endl;
        }
    };
    
    static int
    entry(lua_State *L) {
        cout << "enter entry" << endl;
        Test t;
        luaL_error(L, "there is an error");
        cout << "exit entry" << endl;
        return 0;
    }
    
    int
    main() {
        lua_State *L = luaL_newstate();
        lua_pushcfunction(L, entry);
        if (lua_pcall(L, 0, 0, 0)) {
            cout << "pcall error:" << lua_tostring(L, -1) << endl;
        }
        cout << "after function entry" << endl;
        lua_close(L);
        return 0;
    }
    

    Makefile 如下:

    
    tc:
        g++ -Wall -g -DLUA_CLIB -o tc main.cc ./liblua.a
    tcc:
        g++ -Wall -g -o tcc main.cc ./libluacc.a
    
    clean:
        rm -f tc
        rm -f tcc
    
    • 运行 Lua 的 C 版本库对应的可执行文件 tc 和 Lua 的 C++ 版本库对应的可执行文件 tcc

    运行 tc 输出如下:

    $ ./tc
    enter entry
    Test ctor:0x7ffdfd7b4677
    pcall error:there is an error
    after function entry
    

    运行 tcc 输出如下:

    $ ./tcc
    enter entry
    Test ctor:0x7ffd25d86e47
    Test dctor:0x7ffd25d86e47
    pcall error:there is an error
    after function entry
    

    可发现运行 tcc 时,对象 t 的析构函数执行了。


    总之,要小心处理 Lua 中的异常,特别是在 C++ 中。在 C++ 中,即使 Lua 中的异常使用了 C++ 异常,要捕获 Lua 中抛出的异常,也只能通过 lua_pcall 或者 lua_resume 而不能使用 try catch 。详情阅读这里

    相关文章

      网友评论

        本文标题:C++ 编译 Lua 从而使 Lua 利用 C++ 异常

        本文链接:https://www.haomeiwen.com/subject/ugrrsctx.html