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 。詳情閱讀這里。