该文章是一年前写的,不好意思在原文下面写心得体会了,就把想说的写在这里。


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 */
  ...
}