C++调用lua时lua脚本中的错误处理

2020年10月14日 1504点热度 17人点赞 0条评论

在自己的C++中调用lua脚本时,遇到了一个比较麻烦的问题。因为在lua脚本中有调用C++提供的一些函数导致lua脚本再正常的lua运行环境中是没办法调试运行,但是不调试运行的脚本会有很多问题,比如语法错误,或者运行时的逻辑错误导致表的索引越界。最开始遇到这些问题的时候我只能在脚本中写相当多的print,这样来调试,但是后面脚本越写越复杂这样的调试方法效率就相当低了。

然后谷歌了一下,发现lua本身在运行脚本之前本来就会检查

lua语法错误静态检查

一般在调用脚本之前我们会用luaL_dostring()或者luaL_dofile()载入脚本。这两个函数在载入脚本时会检查脚本的语法是否正确,如果发现语法错误会返回非0值,并且将语法错误压入栈顶。我们想要获取错误提示直接获取栈顶的数据就可以了。

  • C++代码
lua_State * L= nullptr;

/* 初始化 Lua */
L = luaL_newstate();

/* 载入Lua基本库 */
luaL_openlibs(L);

/* 运行脚本 */
int ok = luaL_dofile(L,"./hello.lua");
if(ok != 0)
{
//检查栈顶数据是否为字符串,否则退出
int type = lua_type(L,-1);
if(type != 4)
return;
std::string error = lua_tostring(L,-1);
std::cout << error;
}
  • lua代码
function hello()
printd("hello world");

因为我在lua脚本中没有在函数的结尾加上end,所以运行结果是

[string "function hello()..."]:2: 'end' expected (to close 'function' at line 1) near

其实我这里的print也故意写错了,尝试了一下函数名或者变量名写错了再这里并不会报错。

C++代码中用了lua_type判断栈中数据的类型,以下是所有数据类型的定义

#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8

运行时的错误捕获

lua_call()是一个用于调用lua脚本中指定函数的函数(好像有点绕)。

它的原型是这样int lua_call(lua_state,fparaCount,returnCount) .这个函数并不提供运行时的错误捕获功能。我们得用和它名字很像的lua_pcall(),让我们可以指定一个错误处理的回调函数。所以我们可以利用它来捕获lua运行时的错误。

  • C++代码
//错误回调
int LuaErrorCallBack(lua_State *L)
{
lua_Debug debug = {};
//错误所处的的调用层级
int rank = 0;
//逐层获取lua抛出的错误,直到获取到
while (lua_getstack(L,rank,&debug)) {
rank ++;
}
//判断栈顶是否为字符串类型
int type = lua_type(L,-1);
if(type != 4)
return 0;
std::string error = lua_tostring(L,-1);
std::string errorShort = debug.short_src;
int errorLine = debug.currentline;

std::string errorMsg = "error:" + error + ",errorShort:" + errorShort
+ ",line:" + std::to_string(errorLine);
//将错误信息压人栈
lua_pushstring(L,errorMsg.c_str());
return 1;
}
int main()
{
lua_State * L= nullptr;

/* 初始化 Lua */
L = luaL_newstate();

/* 载入Lua基本库 */
luaL_openlibs(L);

/* 运行脚本 */
int ok = luaL_dofile(L,"./hello.lua");
if(ok != 0)
{
//检查栈顶数据是否为字符串,否则退出
int type = lua_type(L,-1);
if(type != 4)
return;
std::string error = lua_tostring(L,-1);
std::cout << error;

}
//将错误处理函数压入栈
lua_pushcfunction(L,LuaErrorCallBack);
//获取栈顶的位置即错误回调函数的位置
int callBack = lua_gettop(L);

lua_getglobal(L,"hello");
ok = lua_pcall(L,0,0,callBack);
if(ok != 0)
{
std::string error = lua_tostring(L,-1);
std::cout << error << std::endl;
}

lua_close(L);

}
  • lua代码
function hello()
printf("hello");
end
  • 运行结果
error:[string "function hello()..."]:2: attempt to call a nil value (global 'printf'),errorShort:,line:0

看这次我又把print写错了,不过这次你终于发现了。

不过看这里的输出信息,我对lua_debug这个数据结构中的内容理解有一些偏差,待我了解之后在补充上。

大脸猫

半个C++程序员。

文章评论