轉自:http://www.benmutou.com/blog/archives/49
1. Lua的堆棧和全局表
我們來簡單解釋一下Lua的堆棧和全局表,堆棧大家應該會比較熟悉,它主要是用來讓C++和Lua通信的,是的,它們並不認識對方,只能通過堆棧來溝通,就像寫信一樣。
Lua的全局表又是什么呢?可以想象成是一個map哈希表結構,比如Lua有一個變量:
name = “hello”
那么,全局表就存放了”name”和”hello”的對應關系,Lua可以通過name在全局表中查找到hello。應該是這樣的~
2. Lua和C++的第一次通信
-- hello.lua 文件 myName = "beauty girl"
然后,C++想知道Lua叫什么名字,所以,它們必須要通信了。來看看通信流程:
請注意紅色數字,代表通信順序:
1) C++想獲取Lua的myName字符串的值,所以它把myName放到Lua堆棧(棧頂),以便Lua能看到
2) Lua從堆棧(棧頂)中獲取myName,此時棧頂再次變為空
3) Lua拿着這個myName去Lua全局表查找myName對應的字符串
4) 全局表返回一個字符串”beauty girl”
5) Lua把取得的“beauty girl”字符串放到堆棧(棧頂)
6) C++可以從Lua堆棧中取得“beauty girl”,也就是這位美麗的Lua小姐的名字了~
3. 引入頭文件
我們來看看要在C++中使用Lua,需要些什么東西
/* 文件名: HelloLua.h 描 述: Lua Demo 創建人: 笨木頭 (CSDN博客:http://blog.csdn.net/musicvs) 創建日期: 2012.12.24 */ #ifndef __HELLO_LUA_H_ #define __HELLO_LUA_H_ #include "cocos2d.h" extern "C" { #include <lua.h> #include <lualib.h> #include <lauxlib.h> }; using namespace cocos2d; class HelloLua : public CCLayer { public: CREATE_FUNC(HelloLua); virtual bool init(); static CCScene* scene(); }; #endif
4. 開始使用
來看看我們的cpp文件,我們要開始使用Lua了~!
#include "HelloLua.h" CCScene* HelloLua::scene() { CCScene* scene = CCScene::create(); CCLayer* layer = HelloLua::create(); scene->addChild(layer); return scene; } bool HelloLua::init() { lua_State* pL = lua_open(); luaopen_base(pL); luaopen_math(pL); luaopen_string(pL); /* 1.執行Lua腳本,返回0代表成功 */ /* 2.重置棧頂索引 */ /* 3.判斷棧頂的值的類型是否為String, 返回非0值代表成功 */ /* 4.獲取棧頂的值 */ lua_close(pL); return true; }
5. 執行Lua腳本
現在我們來一步步完善我們的代碼,執行Lua腳本很簡單,看看:
bool HelloLua::init() { lua_State* pL = lua_open(); luaopen_base(pL); luaopen_math(pL); luaopen_string(pL); /* 1.執行Lua腳本,返回0代表成功 */ int err = luaL_dofile(pL, "helloLua.lua"); CCLOG("open : %d", err); /* 2.重置棧頂索引 */ lua_settop(pL, 0); lua_getglobal(pL, "myName"); /* 3.判斷棧頂的值的類型是否為String, 返回非0值代表成功 */ /* 4.獲取棧頂的值 */ lua_close(pL); return true; }
6. 重置棧頂索引, 將全局變量放到堆棧中
lua_settop(pL, 0);是為了確認讓棧頂的索引置為0,因為我們操作棧的時候是根據索引來操作的。置0之后,我們入棧的第一個元素的索引就是1。
那,lua_getglobal(pL, “myName”);又是什么呢?咋一看好像是從lua中取得myName這個全局變量的值,但並不是這樣的,雖然最終也是這樣。
我們之前說過了,Lua和C++是不能直接通信的,要通過堆棧來通信。
因此,lua_getglobal(pL, “myName”);只是把myName放到了棧中,然后lua就會通過myName去全局表尋找,找到myName對應的字符串“beauty girl”,再放到棧中。
7. C++取得字符串
我們來看看完整的代碼:
bool HelloLua::init() { lua_State* pL = lua_open(); luaopen_base(pL); luaopen_math(pL); luaopen_string(pL); /* 1.執行Lua腳本,返回0代表成功 */ int err = luaL_dofile(pL, "helloLua.lua"); CCLOG("open : %d", err); /* 2.重置棧頂索引 */ lua_settop(pL, 0); lua_getglobal(pL, "myName"); /* 3.判斷棧頂的值的類型是否為String, 返回非0值代表成功 */ int isstr = lua_isstring(pL, 1); CCLOG("isstr = %d", isstr); /* 4.獲取棧頂的值 */ const char* str = lua_tostring(pL, 1); CCLOG("getStr = %s", str); lua_close(pL); return true; }
lua_getglobal已經完成了很多工作了,現在堆棧上就放着“beauty girl”字符串,我們只要去取就可以了。
獲取堆棧的值有很多種方法,分別對應不同的變量類型:
lua_toboolean
lua_toNumber
lua_tocfunction
lua_tostring
我就不全部舉例了,現在我們要用lua_tostring來獲取棧頂的值。
最后,在AppDelegate.cpp中把默認啟動場景設為我們的HelloLua場景,用調試模式運行項目,將看到以下日志:
open : 0
isstr = 1
getStr = beauty girl
堆棧索引
1. 正數索引,棧底是1
2. 負數索引,棧頂是-1
8. C++調用Lua函數
-- helloLua.lua文件 myName = "beauty girl" helloTable = {name = "mutou", IQ = 125} function helloAdd(num1, num2) return (num1 + num2) end;
C++調用helloAdd函數
/* C++調用lua的函數 */ void HelloLua::demo3() { lua_State* pL = lua_open(); luaopen_base(pL); /* 執行腳本 */ luaL_dofile(pL, "helloLua.lua"); /* 把helloAdd函數對象放到棧中 */ lua_getglobal(pL, "helloAdd"); /* 把函數所需要的參數入棧 */ lua_pushnumber(pL, 10); lua_pushnumber(pL, 5); /* 執行函數,第一個參數表示函數的參數個數,第二個參數表示函數返回值個數 , Lua會先去堆棧取出參數,然后再取出函數對象,開始執行函數 */ lua_call(pL, 2, 1); int iResult = lua_tonumber(pL, -1); CCLOG("iResult = %d", iResult); }
簡單說明一下步驟:
1) 將helloAdd函數放到棧中:lua_getglobal(pL, “helloAdd”) 。(旁白:看吧,我就知道~!)
2) helloAdd有2個參數,我們要把參數傳遞給lua,所以2個參數都要放到棧里。
3) 第2和第3步已經把函數所需要的數據都放到棧里了,接下來只要告訴lua去棧里取數據,執行函數~!
9. Lua調用C++函數
創建一個c++函數先:
public: static int getNumber(int num); int HelloLua::getNumber( int num ) { CCLOG("getNumber num = %d", num); return num + 1; }
現在,我們想在Lua中調用這個函數,得多寫一個函數。
public: static int cpp_GetNumber(lua_State* pL); int HelloLua::cpp_GetNumber( lua_State* pL ) { /* 從棧頂中取一個值 */ int num = (int)lua_tonumber(pL, 1); /* 調用getNumber函數,將返回值入棧 */ lua_pushnumber(pL, getNumber(num)); /* 返回值個數,getNumber只有一個返回值,所以返回1 */ return 1; }
這是怎么回事呢?我們很清楚,Lua和C++只能通過堆棧通信,所以Lua是不可能直接調用getNumber函數的,所以我們建立一個cpp_GetNumber函數作為中介。
cpp_GetNumber函數有一個lua_State* pL參數,有了這個參數,c++就能從Lua的堆棧中取值了
1) 首先,Lua腳本里會調用cpp_GetNumber函數。
2) 當cpp_GetNumber被調用時,一切又回到C++對Lua的操作了,棧頂里會存放函數所需要的參數,取出來用就可以的。
3) Lua調用cpp_GetNumber之后,需要一個結果,當然,這個結果同樣只能存放在棧里,所以理所當然地要把getNumber的結果入棧。
4) 最后,cpp_GetNumber return了一個值,這個值不是函數的執行結果,而是getNumber需要返回值的個數(Lua支持多個返回值的函數)