Lua中一个lua_State就对应一个lua stack,通常情况下一个lua stack就足够了,由此便产生了与C/C++不同的地方:在C/C++中每个函数调用都会有自己对应的stack,在函数调用完成之后,便将其释放掉,因而不需要担心stack会无限制地生长下去;而在C/C++中使用Lua,由于所有的操作都基于同一个stack,因此需要程序员自己在代码中做stack的清理工作。
Lua提供了一对API来帮助程序员操纵stack:lua_gettop和lua_settop。下面给出两种清理栈的方法:
1)为每个操作Lua栈的C/C++函数添加一个栈顶位置参数。在调用该函数时将lua_gettop的结果作为参数传递,在函数调用结束时使用lua_settop以传入的栈顶位置作为参数恢复调用前的栈顶位置。CEGUI中的script module便采用此方法,下面摘录相关代码:
/**//*************************************************************************
Execute script code string
*************************************************************************/
void LuaScriptModule::executeString(const String& str)
{
int top = lua_gettop(d_state);
executeString_impl(str, initErrorHandlerFunc(), top);
cleanupErrorHandlerFunc();
}
void LuaScriptModule::executeString_impl(const String& str, const int err_idx,
const int top)
{
// load code into lua and call it
int error = luaL_loadbuffer(d_state, str.c_str(), str.length(), str.c_str()) ||
lua_pcall(d_state, 0, 0, err_idx);
// handle errors
if (error)
{
String errMsg = lua_tostring(d_state,-1);
lua_settop(d_state,top);
throw ScriptException("Unable to execute Lua script string: '" +
str + "'\n\n" + errMsg + "\n");
}
lua_settop(d_state,top);
}
2)利用局部变量的创建与销毁来自动实现stack清理。下面为局部变量的类型
struct LuaStackAutoPopup
{
lua_State* mBackupLS;;
int mPos;
LuaStackAutoPopup( lua_State* vpSL )
{
mPos = lua_gettop( vpSL );
mBackupLS = vpSL;
}
void Popup( )
{
lua_settop( mBackupLS,mPos );
mBackupLS = NULL;
}
~LuaStackAutoPopup( )
{
if( mBackupLS )
lua_settop( mBackupLS,mPos );
}
};
利用此机制,改造方法1中的executeString:
/**//*************************************************************************
Execute script code string
*************************************************************************/
void LuaScriptModule::executeString(const String& str)
{
LuaStackAutoPopup stack(d_state);
executeString_impl(str, initErrorHandlerFunc());
cleanupErrorHandlerFunc();
}
void LuaScriptModule::executeString_impl(const String& str, const int err_idx)
{
// load code into lua and call it
int error = luaL_loadbuffer(d_state, str.c_str(), str.length(), str.c_str()) ||
lua_pcall(d_state, 0, 0, err_idx);
// handle errors
if (error)
{
String errMsg = lua_tostring(d_state,-1);
throw ScriptException("Unable to execute Lua script string: '" +
str + "'\n\n" + errMsg + "\n");
}
}