Lua接口学习
今天在程序里面使用lua_call,导致程序没有任何错误提示直接退出,找了好久原因,才知道lua_call不是一个受保护的方法,如果其中发生错误,会导致宿主程序直接退出直到看到这篇文章才恍然大悟:最后查看官方文档,更换使用了lua_pcall。没有错误的问题真心可怕啊。。。
为何使用lua_atpanic
当调用无保护的lua_call后,如果调用栈发生错误(lua_error),那么默认行为是直接退出宿主程序。(可以参考这篇文章)
要避免这样的情况,一种方法是定义自己的panic函数,并作为参数调用lua_atpanic;此外为了避免退出宿主程序,自定义的panic函数应该永不返回(通常是做一个长跳转,令其跳转至lua_call调用点,不过这种做法几乎与lua_pcall无异)
lua_atpanic 1. lua_atpanic设置新panic函数并返回旧的panic函数。
2. 当在无保护环境下发生错误,lua调用当前的panic函数并呼叫exit(EXIT_FAILURE)退出宿主程序。
3. 我们可以提供自己的panic函数以避免退出宿主程序。(一种方法是做一个长跳转(long jump))
4. panic函数可以访问位于栈顶的错误讯息。
示例
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
#include <setjmp.h> static jmp_buf jbuf; int
panichandler(lua_State *L)
printf("%s\n", lua_tostring(L, 1));
lua_pop(L, 1);
longjmp(&jbuf, 1);
}
int main(int argc, char *argv[])
lua_State *L;
int ret = 0;
if ((L = lua_open()) == NULL) {
printf("lua_open() failed!\n");
return 1;
} luaL_openlibs(L);
lua_atpanic(L, panichandler);
if (luaL_loadfile(L, "main.lua") == 0) {
if (setjmp(&jbuf) == 0) /* first jmp */
lua_call(L, 0, 0);
else /* second jmp, error */
ret = 1; else
ret = 1; lua_close(L);
return ret;
}
luaL_checkxxx和luaL_argcheck内部调用luaL_error,luaL_error内部调用lua_error,在调用lua_error前会格式化错误信息压入栈顶,最后调用lua_error,lua_error不会返回,而是做一个长跳转。所以在C函数中使用return luaL_error也不会返回,而是跳转到调用lua_pcall点 即lua_pcall返回。
C库函数的例子
int dir(lua_State *L)
{
HANDLE h;
WIN32_FIND_DATAA fd;
const char *dirpath;
dirpath = luaL_checkstring(L, 1); /* 一旦出错立刻压入错误,并从lua_pcall返回,luaL_error也一样 */
if ((h = FindFirstFileA(dirpath, &fd)) == INVALID_HANDLE_VALUE)
return luaL_error(L, "failed to find '%s', win32 error code %d", dirpath, GetLastError());
return 0;
}
lua_error在内部调用luaD_throw函数:
void luaD_throw (lua_State *L, int errcode) {
if (L->errorJmp) {
L->errorJmp->status = errcode;
LUAI_THROW(L, L->errorJmp);
}
else {
L->status = cast_byte(errcode);
if (G(L)->panic) {
resetstack(L, errcode);
lua_unlock(L);
G(L)->panic(L);
}
exit(EXIT_FAILURE);
}
}
1. 当一个跳转点存在时,lua_error将做一个长跳转
2. 否则调用一个panic函数(如果存在),并退出宿主程序。
现在的问题是跳转点何时会存在,位于何处?
答案:LUA库函数中运行于保护模式的函数会设置跳转点,这包括lua_pcall, lua_cpcall, 以及由此函数衍生的一系列宏(如lua_dofile, luaL_dostring等)
观察lua_pcall的源码,该函数调用luaD_rawrunprotected
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
struct lua_longjmp lj;
lj.status = 0;
lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj;
LUAI_TRY(L, &lj,
(*f)(L, ud);
);
L->errorJmp = lj.previous; /* restore old error handler */
return lj.status;
}
这段代码显示了设置跳转点的过程。相比之下无保护的lua_call就没有类似的代码。
1.调用lua_pcall后,如果调用栈上发生任何错误(即lua_error被调用),lua_error将直接跳转至lua_pcall的调用点,并从lua_pcall返回。
2.调用lua_call后发生错误,lua_error将直接调用panic函数并退出宿主程序;除非通过设置自定义panic函数永不返回(通常是做长跳转以避免退出)。
当使用lua_call时,用lua_atpanic为其设置panic函数
为何使用lua_atpanic
当调用无保护的lua_call后,如果调用栈发生错误(lua_error),那么默认行为是直接退出宿主程序。要避免这样的情况,一种方法是定义自己的panic函数,并作为参数调用lua_atpanic;此外为了避免退出宿主程序,自定义的panic函数应该永不返回(通常是做一个长跳转,令其跳转至lua_call调用点,不过这种做法几乎与lua_pcall无异)
1. lua_atpanic设置新panic函数并返回旧的panic函数。
2. 当在无保护环境下发生错误,lua调用当前的panic函数并呼叫exit(EXIT_FAILURE)退出宿主程序。
3. 我们可以提供自己的panic函数以避免退出宿主程序。(一种方法是做一个长跳转(long jump))
4. panic函数可以访问位于栈顶的错误讯息。
示例
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
#include <setjmp.h>
static jmp_buf jbuf;
int
panichandler(lua_State *L)
{
printf("%s\n", lua_tostring(L, 1));
lua_pop(L, 1);
longjmp(&jbuf, 1);
}
int main(int argc, char *argv[])
{
lua_State *L;
int ret = 0;
if ((L = lua_open()) == NULL)
{
printf("lua_open() failed!\n");
return 1;
}
luaL_openlibs(L);
lua_atpanic(L, panichandler);
if (luaL_loadfile(L, "main.lua") == 0)
{
if (setjmp(&jbuf) == 0) /* first jmp */
lua_call(L, 0, 0);
else /* second jmp, error */
ret = 1;
}
else
ret = 1;
lua_close(L);
return ret;
}
0 评论 :
发表评论