lua 报错处理
在给 c++ 程序集成lua库后,使用lua 时,难免会写出来lua 语法级的错误,运行时的错误。这时候就需要 c++ 程序能够对 lua 的错误有所处理,明确地把错误内容显示出来。
c++中获取 lua 错误
lua 库本身,许多函数都可能会返回一个 int 值,如果这个值不是0,则代表 在执行 lua 脚本时,遇到了错误。
比如 luaL_loadfile() lua_pcall() 等
这个返回值,代表一种 lua 错误。 lua 的错误类型定义在
lua.h
/* thread status; 0 is OK */ #define LUA_YIELD 1 #define LUA_ERRRUN 2 #define LUA_ERRSYNTAX 3 #define LUA_ERRMEM 4 #define LUA_ERRERR 5
有这么多种类的 lua 错误,但总之只要返回值是0,就代表没错。
在返回值不是0的情况下,会把 lua 错误的具体内容 放到 lua 栈的栈顶。可以通过把 lua 栈的栈顶元素 lua_tostring() ,把这个内容打印出来,这个字符串就是 lua 报错的内容了。
在此参考 lua程序设计第2版,谢了一个 stackDump()函数,打印出来 lua 和 c语言交互栈的所有内容,便于在lua 报错时,观察 lua 报错的内容。
std::string LuaWrapper::stackDump() { int stackSize = lua_gettop(_luaState); std::string allInfo; for (int index = 1;index <= stackSize;index++) { int t = lua_type(_luaState, index); std::string strInfo; switch (t) { case LUA_TSTRING: { strInfo = lua_tostring(_luaState, index); break; } case LUA_TBOOLEAN: { strInfo = lua_toboolean(_luaState, index) ? "true" : "false"; break; } case LUA_TNUMBER: { lua_Number result = lua_tonumber(_luaState, index); std::stringstream ss; ss << result; ss >> strInfo; break; } default: { strInfo = lua_typename(_luaState, index); break; } }; allInfo = allInfo + strInfo.c_str() + "\n"; printf("%s\t",strInfo.c_str()); } return allInfo; }
由此,可以在 c++ 调用 lua 函数、执行 lua 文件后,根据返回值,来判断是否有 lua 错误,从而对其进行处理。
例:
int err = luaL_loadfile(_luaState, filepath.c_str()) || lua_pcall(_luaState, 0, LUA_MULTRET, 0); if (err != 0) { std::string info = stackDump(); errorHandler(info); }
lua中处理 lua 错误
上面例子中,会直接执行一个 lua 文件里的 内容。这里的 lua 文件代码如下:
main.lua
print("gogo1go"); function _G_GLOBAL_TRACEBACK_(msg) local allErrInfo = "Error: " .. msg .. "\n" .. "Stack: " .. debug.traceback(); print(allErrInfo); LuaWrapper:getInstance():errorHandler(allErrInfo) end function traceback() for level = 1,math.huge do local info = debug.getinfo(level); if not info then break end if info.what == "C" then print(level,"C function"); else print(string.format("[%s]:%d",info.short_src,info.currentline)); end end end function main() print("main"); traceback() local instance = MyClass:new(); instance:testFunc(); instance:delete(); print(tostring(LuaWrapper:getInstance()._testValue)); end xpcall(main,_G_GLOBAL_TRACEBACK_);
上面的lua 文件中,直接通过 xpcall 执行 main() lua函数。
如果运行时有 lua 错误,则会进入 _G_GLOBAL_TRACEBACK_
有了 xpcall 对 lua 进行错误处理的话,在 lua 触发错误后,lua_pcall(_luaState, 0, LUA_MULTRET, 0); 的返回值 err 就不大于0了,因为在 lua 一层已经处理了 lua 的错误。
如果此时依然希望借助 c++ 处理 lua 错误,则需要在 _G_GLOBAL_TRACEBACK_ 手动调用 c++ 错误处理函数。
LuaWrapper:getInstance():errorHandler(allErrInfo)
lua报错的处理形式
class LuaWrapper 里,我实现了一个函数,用于统一处理 lua报错。并且把这个函数用 tolua++ 导出过,因此 无论是 c++ 里触发了 lua 错误,还是 lua 里面处理了 lua 错误,都可以调用此函数 做错误处理。
void LuaWrapper::errorHandler(std::string errMsg) { #ifdef WIN32 std::wstring errMsgW = StringToWString(errMsg); MessageBox(nullptr, errMsgW.c_str(),L"lua error!", MB_OK); #endif //WIN32 }
根据应用场景的不同,错误处理的形式也不一样。
在游戏开发的情况下,我比较推荐把错误非常明显的现出来。如果仅仅是把lua 错误堆栈和信息打印出来,很可能早出触发了错误而不知,后面的逻辑整体混乱,导致错误难于排查。
因此, 在 win32 平台下,通过 MessageBox() 来显示一个 明显的错误比较合适。
Android 下使用 AlertDialog,iOS下使用 UIAlertController ,都可以达到把错误明显显示出来的目的。
例如,在 main.lua 里随便写一个 错误 (第5行)
print("gogo1go"); fdsfd function _G_GLOBAL_TRACEBACK_(msg) local allErrInfo = "Error: " .. msg .. "\n" .. "Stack: " .. debug.traceback(); print(allErrInfo); LuaWrapper:getInstance():errorHandler(allErrInfo) end function traceback() for level = 1,math.huge do local info = debug.getinfo(level); if not info then break end if info.what == "C" then print(level,"C function"); else print(string.format("[%s]:%d",info.short_src,info.currentline)); end end end function main() print("main"); traceback() local instance = MyClass:new(); instance:testFunc(); instance:delete(); print(tostring(LuaWrapper:getInstance()._testValue)); end xpcall(main,_G_GLOBAL_TRACEBACK_);
则会调用 void LuaWrapper::errorHandler(std::string errMsg) 函数,显示一个非常明显的错误提示: