前兩篇文章中,已經介紹了使用vs2010編譯lua5.1源碼生成lua.lib 和 vs項目中使用c++調用lua,可以保證demo在vs上運行起來了。這裡再詳細介紹下c++和lua之間的相互調用以及原理。
c++與lua直接的調用,實際上通過一個棧結構來傳遞數據,該棧結構棧頂的索引值為-1,向棧底方向索引值依次為-1 ,-2......棧頂索引為1.棧結構裡可以放函數,表,字符串,整形等各種lua的基本數據。
print(hello world from lua file) table1 = {} table1[dinner] = rice table1[hahha] = ri123ce gScreenW = 1280; function getIntegerSumFuncs(a ,b) return a + b end avg ,sum = average(10 ,20 ,30 ,40 ,50)
int luaL_dofile (lua_State *L, const char *filename);//運行lua文件,L是當前已經創建的棧結構。返回0,運行文件正常;返回1,代表出現異常。
void lua_setglobal (lua_State *L, const char *name);//從堆棧上彈出一個值,並將其設到全局變量 name 中
void lua_getglobal (lua_State *L, const char *name);//把全局變量 name 裡的值壓入堆棧,棧頂值為-1。
const char *lua_tostring (lua_State *L, int index);//在棧L的索引值為index處取值並轉化成C字符串(lua_tointeger等類似)
void lua_settop (lua_State *L, int index);//參數允許傳入任何可接受的索引以及 0。它將把堆棧的棧頂設為這個索引。 如果新的棧頂比原來的大,超出部分的新元素將被填為 nil 。 如果 index 為 0 ,把棧上所有元素移除。
int lua_gettop (lua_State *L);//返回堆棧上的元素個數(返回 0 表示堆棧為空)
int lua_next (lua_State *L, int index);從棧上彈出一個 key(鍵), 然後把索引指定的表中 key-value(健值)對壓入堆棧 (指定 key 後面的下一 (next) 對)。 如果表中以無更多元素, 那麼 lua_next 將返回 0 (什麼也不壓入堆棧)。
void lua_pushinteger (lua_State *L, lua_Integer n);//把 n 作為一個數字壓棧。
void lua_close (lua_State *L);//銷毀指定 Lua 狀態機中的所有對象
如果需要查詢其他函數,推薦到Lua 5.1 參考手冊,很詳細也比較准確。
//獲取lua全局string const char* getLuaGlobalString(char *fileName ,char *varName) { lua_State *L = lua_open(); luaL_openlibs(L); //加載並運行test.lua文件 int isOpen = luaL_dofile(L ,fileName); if (isOpen == 0) { printf(error loading lua); } //將棧頂的索引設置為該index,如果index傳0,移除棧上所有元素 lua_settop(L ,0); //把全局變量 allGlobalChar 裡的值壓入堆棧。 char *allGlobalChar = varName; lua_getglobal(L ,allGlobalChar); int stateCode = lua_isstring(L ,1); if (stateCode != 1) { printf(open lua error code:%d ,stateCode); return NULL; } const char* s = lua_tostring(L ,1); printf(get global screenW is:%s ,s); lua_pop(L ,1); lua_close(L); return s; }調用代碼:
getLuaGlobalString(test.lua ,gScreenW);
const char* getLuaVarOfTable(const char *fileName,const char *tableName ,const char *keyName) { lua_State *L = lua_open(); luaL_openlibs(L); int isOpen = luaL_dofile(L ,fileName); if (isOpen != 0) { printf(error open lua file); return NULL; } lua_settop(L ,0);//清空棧 lua_getglobal(L ,tableName); int stateCode = lua_istable(L ,-1);//取棧頂 if (stateCode != 1) { printf(get table failed code:%d ,stateCode); return NULL; } lua_pushstring(L ,keyName); lua_gettable(L ,-2); //取棧頂下一個元素 const char* valueStr = lua_tostring(L ,-1); //取棧頂 printf(get lua table key-value %s-%s ,keyName ,valueStr); lua_pop(L ,-1); return valueStr; }調用代碼:
getLuaVarOfTable(test.lua ,table1 ,dinner);
string getLuaVarTable(const char* fileName ,const char *tableName ,char *result) { lua_State *L = lua_open(); luaL_openlibs(L); int isOpen = luaL_dofile(L ,fileName); if (isOpen != 0) { printf(error open lua file); return NULL; } lua_getglobal(L ,tableName); int size = lua_gettop(L); lua_pushnil(L); int index = 0; while(lua_next(L ,size)) { const char* key = lua_tostring(L ,-2);//取棧頂下一個元素 const char* value = lua_tostring(L , -1); //取棧頂 //使用memcpy ,index記錄地址游標 memcpy(&result[index] ,key ,strlen(key)); index += strlen(key); //因為sizeof取字符串,最後會有/0占一個字節,所以應該減去1 memcpy(&result[index] ,- ,sizeof(-) - 1); index += 1; memcpy(&result[index] ,value ,strlen(value)); index += strlen(value); lua_pop(L ,1); } result[index] = 0; cout <調用代碼:
char* reslut = (char*)malloc(100 * sizeof(char)); getLuaVarTable(test.lua ,table1 ,reslut); free(reslut);
六、cpp調用lua中的函數
//要調用一個函數請遵循以下協議: 首先,要調用的函數應該被壓入堆棧; // 接著,把需要傳遞給這個函數的參數按正序壓棧; 這是指第一個參數首先壓棧。 // 最後調用一下 lua_call; nargs 是你壓入堆棧的參數個數。 // 當函數調用完畢後,所有的參數以及函數本身都會出棧。 而函數的返回值這時則被壓入堆棧。 // 返回值的個數將被調整為 nresults 個, 除非 nresults 被設置成 LUA_MULTRET。 // 在這種情況下,所有的返回值都被壓入堆棧中。 Lua 會保證返回值都放入棧空間中。 // 函數返回值將按正序壓棧(第一個返回值首先壓棧), 因此在調用結束後,最後一個返回值將被放在棧頂。 const char* callLuaFunc(const char* fileName ,const char* functionName) { lua_State *L = lua_open(); luaL_openlibs(L); int isOpen = luaL_dofile(L ,fileName); if (isOpen != 0) { printf(error open lua file %s ,fileName); return NULL; } lua_getglobal(L ,functionName);//如果表和函數重名,會怎麼樣 lua_pushinteger(L ,12); lua_pushinteger(L ,3); //第一個參數:函數的個數 第二個參數:函數的返回值個數 lua_call(L ,2 ,1); int result = lua_tointeger(L ,-1); printf(call lua func result : %d ,result); return NULL; }調用代碼:
callLuaFunc(test.lua ,getIntegerSumFuncs);
七、lua調用cpp函數計算平均值
//cpp計算平均值的函數 int average(lua_State* L) { int n = lua_gettop(L); double sum = 0; int i; for (i = 1; i <= n; i ++) { if (!lua_isnumber(L ,i)) { lua_pushstring(L ,incorrect argument to avg); lua_error(L); } sum += lua_tonumber(L ,i); } lua_pushnumber(L ,sum / n); lua_pushnumber(L ,sum); //這裡一定要返回元素個數,若返回0則找不到結果 return 2; } //lua調用cpp計算平均值,調用入口 int averageMain() { lua_State* L = lua_open(); luaL_openlibs(L); //注冊lua函數,實際上是將average push到棧結構,然後set average函數為global函數 lua_register(L ,average ,average); //運行腳本 luaL_dofile(L ,test.lua); //把全局變量 avg 裡的值壓入堆棧 lua_getglobal(L ,avg); // 棧頂是-1,棧底是1 cout << cpp show: avg is << lua_tointeger(L ,-1) <代碼調用: averageMain();