最近一个COCOS2DX项目有了较大的变化,整个项目的数据层都从C++层移到了LUA层。这下问题就来了,如何在C++层访问LUA的数据层呢?而我作为一个LUA的新人,那就纠结了,那尼马还不得啃好几本书?如果我告诉上司,我需要一周的时间,那估计要被炒掉了。但没法,任务分配了,开始干吧。
关于在c++代码中访问LUA函数的教程,可以说遍布网络的每个角落,大家会去教你如何使用lua_pcall, lua_getglobal, lua_pushnumber等。基本要求如下:
1、普通全局函数的访问(这个容易,如果只有一两个参数,一两个返回值,都能应付)
2、表内函数的访问(这个尼马,又和表扯到一起了,冒似LUA里的包也和表是同一概念,尼马难度提高了)
3、参数和返回值的类型支持INT,STRING,TABLE,参数和返回值的数量支持任意数量。(数量都不是问题,按顺序PUSH就完了,如何PUSH一个table呢????这个难度就有点高了,估计亲你得回去和度娘好好亲热一番。如果这个table嵌套了TABLE呢???是不是开始蛋疼了?肯定会有某种需求,要向LUA函数传递一个二维,甚至三维数组,你敢说这种需求不会出现?)
作为程序员的第一感觉,我需要做一个类来管理这所有的事情。于是我设计了一个类LuaAction用来模拟整个函数的调用过程。
LuaAction已经上传了,下载页面:http://download.csdn.net/detail/sunshine7858/6357979
基本思路如下:
LuaAction lua; //创建一个LuaAction对象 lua.setModule(module); //设置包名,如果函数没有包名,那就跳过这行 lua.setFunction(func); //设置要访问的函数名, lua.addStringParam(argString); //添加第一个string参数,如果没有就跳过 lua.addIntParam(argInt); //添加第二个int参数,如果没有就跳过 lua.addTableParam(argTable); //添加第三个table参数,如果没有就跳过 ....... //添加更多的参数 lua.executeFunction(resultnum); //核心函数,要传入返回值的数量,然后找到要执行的函数,压入参数,读取返回值。 string retuslt0 = lua.getStringResult(0); //获取第1个SRING返回值 int result1 = lua.getIntResult(1); //获取第2个INT返回值 table result2 = lua.getTableResult(2); //获取第3个table返回值
....
而其中最难的地方,则是如何通过递归加载整个table表。而详细的实现细节比较复杂,就不在这里赘述了。基本上有了这个类,可以访问LUA中的所有公共函数,就像你在LUA层一样。由于LuaAction已经实现了多层表的嵌套,你既可以传入一个多层的TABLE,也可以返回一个多层的TABLE。这样就大大方便了LUA和C++的交互。
但LuaAction在使用前,必须调用一次他的初始化函数:
void LuaAction::initState(LuaState* state);
剩余的事情就和上面的流程一样了。
有了这个类,可以说一劳永逸,我再也不用去管什么lua_gettop(),什么lua_pushstring()等。都见鬼去吧,哥再也不和你们玩了!
但时间长了后,问题又出现了,发现大家经常会访问一些简单的函数,可能只有一个参数,或没有参数,或只是想返回一个值。但在调用时,我还是要了解LuaAction的全部对外接口。而且很可能只是一个简单的string getName()函数我确要写近5行的代码,实在不划算啊,看着也累啊。好吧,为了提高代码的重用性,我将这些常用的函数都包装了起来。创建了一个LuaAactionFactory类,用来访问那些常用的函数。如:
static void void_func_void(const char* module, const char* func); static void void_func_int(const char* module, const char* func, int arg1); static int int_func_void(const char* module, const char* func); static int int_func_int(const char* module, const char* func, int arg1); ........
举个例子:Lua中有一个函数如下
function getName(index) return tNames[index] end
LuaAction的访问代码:
LuaAction lua; lua.setModule(module); lua.setFunction(func); lua.addIntParam(arg1); lua.executeFunction(1); return lua.getStringResult(0);
LuaActionFactory访问代码:
int ret = LuaActionFactory::int_func_int(NULL, "getName", 1)
最后对2维数组的传递和获取做一下简单的演示:
有LUA函数如下: function func(t) t[1] = 1 return t end LuaAction的返问代码: //创建一个要传递给函数的table参数 LuaParamTable* table = new LuaParamTable(); //以下设置各种属性 table->setInt("key1", 11); table->setString("key2", "value1"); table->setInt(5, 22); table->setString(6, "value2"); //创建子表 LuaParamTable* subtable = new LuaParamTable(); subtable->setInt("key3", 5556); subtable->setString("key4", "value1"); subtable->setInt(3, 444); subtable->setString(4, "value3"); //将子表挂到主表中 table->setTable("table", subtable); LuaAction lua; //启动LuaAction调用函数 lua.setFunction("func"); lua.addTableParam(table); //压入表参数 lua.executeFunction(1); //执行函数 LuaParamTable tResult; lua.getTableResultAndClear(&tResult, 0); //获取表对象 int a = tResult.getIntByIndex(1); //检查在LUA中的赋值 //将返回值与之前的赋值情况进行对比 int value1 = tResult.getIntByKey("key1"); string value2 = tResult.getStringByKey("key2"); int value3 = tResult.getIntByIndex(5); string value4 = tResult.getStringByIndex(6); LuaParamTable tSubResult; tResult.swapTableByKey("table", &tSubResult); int value5 = tSubResult.getIntByKey("key3"); string value6 = tSubResult.getStringByKey("key4"); int value7 = tSubResult.getIntByIndex(3); string value8 = tSubResult.getStringByIndex(4); 切记:作为表参数时,LuaParamTable是new出来的,因为他的生命周期要交给LuaAction去管理。作为返回值时,LuaParamTable是直接在栈上创建的,因为他自己要管理自己的生命周期。
相信有了以上的工具,基本上大部分的LUA函数都能访问了。