在给 c++ 程序集成lua库后,使用lua 时,难免会写出来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 文件代码如下:
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)
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) 函数,显示一个非常明显的错误提示: