该文章是一年前写的,不好意思在原文下面写心得体会了,就把想说的写在这里。
1,在你的程序中嵌入lua时,最好使用由你的编译器编译lua源代码得到的库文件(lua.lib)。
这是因为,在lua的异常处理机制里面会使用一些宏,在不同的编译环境下这些宏有不同的定义,例如:
#if defined(__cplusplus)
/* C++ exceptions */
#define LUAI_THROW(L,c) throw(c)
#define LUAI_TRY(L,c,a) try { a } catch(...) \
{ if ((c)->status == 0) (c)->status = -1; }
#define luai_jmpbuf int /* dummy variable */
#elif defined(LUA_USE_ULONGJMP)
/* in Unix, try _longjmp/_setjmp (more efficient) */
#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
#define luai_jmpbuf jmp_buf
#else
/* default handling with long jumps */
#define LUAI_THROW(L,c) longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a }
#define luai_jmpbuf jmp_buf
#endif
如果你的程序是C++编写的,但是链接的却是在C环境下编译出来的lua.lib,那么表面上工作正常,但是一涉及到异常处理,就会出现很多未知的问题。
2,在你的代码中使用lua API时,由于lua API有抛出异常的可能,会导致“抛出异常处”的后面的代码不能执行到。例如:
struct foobar
{
const char *a;
const char *b;
}
foobar* pObj = new foobar;
pObj->a = lua_tostring(L, 1);
pObj->b = lua_tostring(L, 2);
...
delete pObj;
如果在执行lua_tostring()时抛出了异常,那么后面的 “delete pObj;" 就不能执行到,造成内存泄漏。
对于这样的临时内存块,有一个解决办法是使用 lua_newuserdata 来申请一段由lua维护的内存块,它会被GC操作回收掉。例如:
foobar* pObj = lua_newuserdata(L, sizeof(foobar));
pObj->a = lua_tostring(L, 1);
pObj->b = lua_tostring(L, 2);
上面的代码有另外一个潜在问题,pObj对象没有初始化,一旦执行lua_tostring()时抛出了异常,就会使得 pObj->a 或者 pObj->b 的值是未初始化的,不管是在宿主代码中访问pObj对象,还是在lua脚本中访问pObj对象,都可能带来未知的问题。正确的写法是这样:
foobar* pObj = lua_newuserdata(L, sizeof(foobar));
pObj->a = NULL; //首先做初始化
pObj->b = NULL;
pObj->a = lua_tostring(L, 1);
pObj->b = lua_tostring(L, 2);
3,lua API函数可能会抛出异常,这些异常应该由 lua_pcall 或者 lua_resume 来捕获,所以要确保lua API函数的调用处于某次 lua_pcall 或者 lua_resume 中。
下面是最常见的创建lua环境的代码,但是这段代码就是有风险的,因为 luaL_openlibs(L) 有可能抛出异常,但是没有捕获异常:
lua_State *L = luaL_newstate();
if (L)
{
luaL_openlibs(L);
}
正确的做法是,把你的代码写到一个 lua_CFunction 中,然后用 lua_pcall 来调用它。而 lua_CFunction 中需要使用的参数,则应该用 void * 通过 lua_pushlightuserdata 来传递。
lua官方的解释器的程序源码,就为我们示范了这种正确的做法:
//辅助函数
static int pmain (lua_State *L)
{
int argc = (int)lua_tointeger(L, 1);
char **argv = (char **)lua_touserdata(L, 2);
...
luaL_openlibs(L); /* open standard libraries */
...
}
//主函数
int main (int argc, char **argv)
{
int status, result;
lua_State *L = luaL_newstate(); /* create state */
lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */
lua_pushinteger(L, argc); /* 1st argument */
lua_pushlightuserdata(L, argv); /* 2nd argument */
status = lua_pcall(L, 2, 1, 0); /* do the call */
result = lua_toboolean(L, -1); /* get result */
...
}