前段时间封装了一个意在跨平台,且满足自己需求的很light的LuaEngine,对于表参数和表返回值留了白,想找时间研究一下,近日终于弄好。
首先我对C++的参数和返回值做了一个封装
enum
{
SD_NUMBER = 0, // 数字类型
SD_STRING, // 字符串类型
SD_TABLE, //表
};
struct SSDTable
{
int nNum;
void* pValue;
};
// 脚本参数对象
struct SScriptParamObj
{
int nType; // 参数类型, SD_NUMBER 或者 SD_STRING
union UScriptParam // 参数值
{
int nNumber; // 数字
char szString[64]; // 字符串
SSDTable stTable;
} unValue;
SScriptParamObj()
{
memset(this, 0, sizeof(*this));
}
~SScriptParamObj()
{
}
void operator = (int nValue)
{
nType = SD_NUMBER;
unValue.nNumber = nValue;
}
void operator = (const char *str)
{
nType = SD_STRING;
unValue.szString[0] = 0;
if (str != NULL)
{
strncpy(unValue.szString, str, sizeof(unValue.szString));
}
}
void operator = ( SSDTable& pT )
{
nType = SD_TABLE;
unValue.stTable.nNum = pT.nNum;
unValue.stTable.pValue = (void *)pT.pValue;
}
};
需要细心一点的就是,对于嵌套表的处理,不用说大家也就知道了--递归。
下面的这个函数是C++调用Lua的函数,Lua函数的参数和返回值都作为C++的参数
bool CLuaScript::CallFunction(const char *szFuncName, SScriptParamObj *pIn,
int nInNum, SScriptParamObj *pRet, int nRetNum)
{
if (szFuncName == NULL)
{
return false;
}
assert(m_pManager->GetMasterState());
assert(m_pThreadState);
lua_getglobal(m_pThreadState, szFuncName);
for (int i = 0; i < nInNum; i++)
{
// 参数的三种类型
switch (pIn[i].nType)
{
case SD_NUMBER:
lua_pushnumber(m_pThreadState, pIn[i].unValue.nNumber);
break;
case SD_STRING:
lua_pushstring(m_pThreadState, pIn[i].unValue.szString);
break;
case SD_TABLE:
// 现在栈顶创建一个新的表
lua_newtable(m_pThreadState);
int nSize = pIn[i].unValue.stTable.nNum;
SScriptParamObj* pData = (SScriptParamObj*)pIn[i].unValue.stTable.pValue;
PushTable(pData, nSize);
break;
}
}
int nStatus = lua_pcall(m_pThreadState, nInNum, nRetNum, 0);
for (int i = nRetNum - 1; i >= 0; i--)
{
// 参数的三种类型,pop的顺序,完全靠直觉
switch (pRet[i].nType)
{
case SD_NUMBER:
pRet[i].unValue.nNumber = lua_tonumber(m_pThreadState, -1);
lua_pop(m_pThreadState, 1);
break;
case SD_STRING:
strcpy(pRet[i].unValue.szString, lua_tostring(m_pThreadState, -1));
lua_pop(m_pThreadState, 1);
break;
case SD_TABLE:
ReturnTable(&pRet[i]);
lua_pop(m_pThreadState, 1);
break;
}
}
if (nStatus != 0)
{
FormatError();
OutputError("Runtime Error:");
return false;
}
return true;
}
处理表作为输入参数,对于嵌套表的处理,请大家详细的看下代码就明白了
void CLuaScript::PushTable(SScriptParamObj *pIn, int nInNum)
{
for (int i = 0; i < nInNum; i++)
{
// 参数的三种类型
switch (pIn[i].nType)
{
case SD_NUMBER:
// 添加key和value,下标从1开始
lua_pushnumber(m_pThreadState, i + 1);
lua_pushnumber(m_pThreadState, pIn[i].unValue.nNumber);
lua_rawset(m_pThreadState, -3);
break;
case SD_STRING:
lua_pushnumber(m_pThreadState, i + 1);
lua_pushstring(m_pThreadState, pIn[i].unValue.szString);
lua_rawset(m_pThreadState, -3);
break;
case SD_TABLE:
lua_pushnumber(m_pThreadState, i + 1);
lua_newtable(m_pThreadState);
int nSize = pIn[i].unValue.stTable.nNum;
SScriptParamObj* pData = (SScriptParamObj*)pIn[i].unValue.stTable.pValue;
PushTable(pData, nSize);
lua_rawset(m_pThreadState, -3);
break;
}
}
}
表作为结果返回的时候,要注意下面的情况了是否返回表结构的逻辑,程序员应该知道的,如果是 表结构,请务必手动删除分配的内存,而且
在多层的嵌套表结构中,要逐层清理。大概意思就是在引擎中会new内存,而这块内存,引擎并不知道在什么时候释放,需要程序员去手动的释放
void CLuaScript::ReturnTable(SScriptParamObj* pRet)
{
// 获取到表的索引
int nNum = 0;
int nIndex = lua_gettop(m_pThreadState);
lua_pushnil(m_pThreadState);
// 先得到数组的长度
while (lua_next(m_pThreadState, nIndex) != 0)
{
nNum++;
//移除 'value' ;保留 'key' 做下一次迭代
lua_pop(m_pThreadState, 1);
}
nIndex = lua_gettop(m_pThreadState);
// 这时候栈顶还是表
lua_pushnil(m_pThreadState);
SScriptParamObj* pObject = new SScriptParamObj[nNum];
pRet->unValue.stTable.pValue = pObject;
pRet->unValue.stTable.nNum = nNum;
nNum = 0;
while (lua_next(m_pThreadState, nIndex) != 0)
{
// 'key' (索引-2) 和 'value' (索引-1)
// 只对Value感兴趣
if (lua_type(m_pThreadState, -1) == LUA_TSTRING)
{
pObject[nNum++] = lua_tostring(m_pThreadState, -1);
}
else if (lua_type(m_pThreadState, -1) == LUA_TNUMBER)
{
pObject[nNum++] = (int)lua_tonumber(m_pThreadState, -1);
}
else if (lua_type(m_pThreadState, -1) == LUA_TTABLE)
{
ReturnTable(&pObject[nNum++]);
}
else
{
nNum++;
}
//移除 'value' ;保留 'key' 做下一次迭代
lua_pop(m_pThreadState, 1);
}
}