C++ 編譯 Lua 從而使 Lua 利用 C++ 異常

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 時(shí),也有提到使用 C++ 異常: According to the Lua Wiki, if Lua 5.1 or later is compiled as C++, it will use C++ exceptions. 。

閱讀這篇博客時(shí),云大也提到:

看 ldo.c 前面的 LUAI_THROW LUAI_TRY 等宏就是做的這個(gè)事情。所以,如果你用 C++ 做宿主語言,就應(yīng)該用 C++ 編譯器編譯 Lua 庫。

并且在下面一段中的一句話給出了如果用 C++ 做宿主語言,應(yīng)該用 C++ 編譯 Lua 的原因:

Lua 在內(nèi)部發(fā)生異常時(shí),VM 會(huì)在 C 的 stack frame 上直接跳至之前設(shè)置的恢復(fù)點(diǎn),然后 unwind lua vm 層次上的 lua stack 。lua stack (CallInfo 結(jié)構(gòu))在捕獲異常后是正確的,但 C 的 stack frame 的處理未必如你的宿主程序所愿。也就是 RAII 機(jī)制很可能沒有被觸發(fā)。

上面這段話的意義在于在 Lua 中使用 C++ 異常,如果 Lua 拋出異常,此時(shí)走 C++ 異常機(jī)制,可確保對(duì)象被正確銷毀,即析構(gòu)函數(shù)被調(diào)用,也就保證了 RAII 機(jī)制。

舉個(gè)例子,如下代碼片段,在創(chuàng)建 C++ 對(duì)象 t 后發(fā)生了異常,此時(shí)使用 C++ 編譯的 Lua 庫,可保證 t 的析構(gòu)函數(shù)會(huì)被調(diào)用。

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

下面測(cè)試使用 C 和 C++ 編譯的 Lua 庫,來驗(yàn)證對(duì)象的析構(gòu)函數(shù)是否會(huì)被調(diào)用。

  • 編譯 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
  • 運(yùn)行 Lua 的 C 版本庫對(duì)應(yīng)的可執(zhí)行文件 tc 和 Lua 的 C++ 版本庫對(duì)應(yīng)的可執(zhí)行文件 tcc 。

運(yùn)行 tc 輸出如下:

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

運(yùn)行 tcc 輸出如下:

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

可發(fā)現(xiàn)運(yùn)行 tcc 時(shí),對(duì)象 t 的析構(gòu)函數(shù)執(zhí)行了。


總之,要小心處理 Lua 中的異常,特別是在 C++ 中。在 C++ 中,即使 Lua 中的異常使用了 C++ 異常,要捕獲 Lua 中拋出的異常,也只能通過 lua_pcall 或者 lua_resume 而不能使用 try catch 。詳情閱讀這里。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容