前兩篇文章中,已經介紹了使用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();