最近一個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] = 1return tendLuaAction的返問代碼://建立一個要傳遞給函數的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函數都能訪問了。