Lua是一個嵌入式的腳本語言,它不只能夠單獨使用還能與其它語言混合調用。
Lua與其它腳本語言相比,其突出優點在於:html
1. 可擴展性。Lua的擴展性很是卓越,以致於不少人把Lua用做搭建領域語言的工具(注:好比遊戲腳本)。Lua被設計爲易於擴展的,能夠經過Lua代碼或者 C代碼擴展,Lua的不少功能都是經過外部庫來擴展的。Lua很容易與C/C++、java、fortran、Smalltalk、Ada,以及其餘語言接口。java
2. 簡單。Lua自己簡單,小巧;內容少但功能強大,這使得Lua易於學習,很容易實現一些小的應用。他的徹底發佈版(代碼、手冊以及某些平臺的二進制文件)僅用一張軟盤就能夠裝得下。linux
3. 高效率。Lua有很高的執行效率,統計代表Lua是目前平均效率最高的腳本語言。ios
4. 與平臺無關。Lua幾乎能夠運行在全部咱們據說過的系統上,如NextStep、OS/二、PlayStation II (Sony)、Mac OS-九、OS X、BeOS、MS-DOS、IBM mainframes、EPOC、PalmOS、MCF5206eLITE Evaluation Board、RISC OS,及全部的Windows和Unix。Lua不是經過使用條件編譯實現平臺無關,而是徹底使用ANSI (ISO) C,這意味着只要你有ANSI C編譯器你就能夠編譯並使用Lua。macos
要在C++中使用Lua很是簡單,無論是GCC,VC仍是C++Builder, 最簡單的方法就是把Lua源碼中除lua.c,luac.c和print.c之外的全部c文件與你的代碼一塊兒編譯連接(或加入到工程中)便可。
固然,爲了方便維護,最好仍是先把Lua編譯成庫文件再加入工程。方法以下:編程
GCCwindows
直接在Lua所在目錄下make [環境]數組
這裏的[環境]能夠是:aix ansi bsd freebsd generic linux macosx mingw posix solaris安全
若是你的環境不在這個列表中,你能夠試試ansi或posix。數據結構
VC
在命令行環境下進入Lua所在目錄,執行etc\luavs.bat編譯。
C++Builder
請看個人Blog,如何在C++Builder裏編譯Lua
頭文件
由於Lua是用C語言寫的,除非編譯lua庫時指定編譯器強制以C++方式編譯,不然在C++工程中應該這樣包含lua頭文件:
1. extern "C" {
2. #include "lua.h"
3. #include "lualib.h"
4. #include "lauxlib.h"
5. }
例一,簡單運行Lua代碼
1. extern "C" {
2. #include "lua.h"
3. #include "lualib.h"
4. #include "lauxlib.h"
5. }
6.
7. #include <iostream>
8. #include <string>
9. using namespace std;
10.
11.int main()
12.{
13. lua_State *L = lua_open(); //初始化lua
14. luaL_openlibs(L); //載入全部lua標準庫
15.
16. string s;
17. while(getline(cin,s)) //從cin中讀入一行到s
18. {
19. //載入s裏的lua代碼後執行
20. bool err = luaL_loadbuffer(L, s.c_str(), s.length(),
21. "line") || lua_pcall(L, 0, 0, 0);
22. if(err)
23. {
24. //若是錯誤,顯示
25. cerr << lua_tostring(L, -1);
26. //彈出錯誤信息所在的最上層棧
27. lua_pop(L, 1);
28. }
29. }
30.
31. lua_close(L);//關閉
32. return 0;
33.}
這已是一個功能完備的交互方式Lua解釋器了。
輸入print "hello world"
輸出hello world
輸入for i=1,10 do print(i) end
輸出從1到10
要調用Lua,首先要使用lua_open(對於5.0之後版本的Lua,建議使用luaL_newstate代替)產生一個lua_State,在使用完後調用lua_close關閉。
全部Lua與C之間交換的數據都是經過Lua中的棧來中轉的。
在本例中:
luaL_loadbuffer的功能是載入並編譯內存中的一段Lua代碼,而後做爲一個代碼塊(稱爲chunk)壓入棧中,其中的最後一個參數做爲代碼塊的名稱用於調試。和它功能相似的還有luaL_loadfile(載入文件),luaL_loadstring(載入字符串,本例中也可用它代替luaL_loadbuffer)。它們有一個相同的前綴:luaL_,爲了簡化編程,Lua C API將庫中的核心函數包裝後做爲輔助函數提供一些經常使用功能,它們的形式都是luaL_*,如這裏的三個luaL_load*都調用了lua_load。
lua_pcall從棧頂取得函數並執行,若是出錯,則返回一個非0值並把錯誤信息壓入棧頂。關於它的更詳細信息會在「例三,在C++中調用Lua子函數」中介紹。
若是宿主程序檢測到錯誤,就用lua_tostring從棧頂取得錯誤信息轉成字符串輸出,而後彈出這個錯誤信息。
lua_tostring裏的第二個參數指定要操做的數據處於棧的哪一個位置,由於全部的數據只能經過棧來交換,因此不少Lua的C API都會要求指定棧的位置。1表示在棧中的第一個元素(也就是第一個被壓入棧的),下一個索引是2,以此類推。咱們也能夠用棧頂做爲參照來存取元素,使用負數:-1指出棧頂元素(也就是最後被壓入的),-2指出它的前一個元素,以此類推。
例二,與Lua交換數據
1. extern "C" {
2. #include "lua.h"
3. #include "lualib.h"
4. #include "lauxlib.h"
5. }
6.
7. #include <iostream>
8. #include <string>
9. using namespace std;
10.
11.int main()
12.{
13. //Lua示例代碼
14. char *szLua_code =
15. "r = string.gsub(c_Str, c_Mode, c_Tag) --宿主給的變量 "
16. "u = string.upper(r)";
17. //Lua的字符串模式
18. char *szMode = "(%w+)%s*=%s*(%w+)";
19. //要處理的字符串
20. char *szStr = "key1 = value1 key2 = value2";
21. //目標字符串模式
22. char *szTag = "<%1>%2</%1>";
23.
24. lua_State *L = luaL_newstate();
25. luaL_openlibs(L);
26.
27. //把一個數據送給Lua
28. lua_pushstring(L, szMode);
29. lua_setglobal(L, "c_Mode");
30. lua_pushstring(L, szTag);
31. lua_setglobal(L, "c_Tag");
32. lua_pushstring(L, szStr);
33. lua_setglobal(L, "c_Str");
34.
35. //執行
36. bool err = luaL_loadbuffer(L, szLua_code, strlen(szLua_code),
37. "demo") || lua_pcall(L, 0, 0, 0);
38. if(err)
39. {
40. //若是錯誤,顯示
41. cerr << lua_tostring(L, -1);
42. //彈出棧頂的這個錯誤信息
43. lua_pop(L, 1);
44. }
45. else
46. {
47. //Lua執行後取得全局變量的值
48. lua_getglobal(L, "r");
49. cout << "r = " << lua_tostring(L,-1) << endl;
50. lua_pop(L, 1);
51.
52. lua_getglobal(L, "u");
53. cout << "u = " << lua_tostring(L,-1) << endl;
54. lua_pop(L, 1);
55. }
56. lua_close(L);
57. return 0;
58.}
這段代碼把字符串中的key=value字符串所有轉換成XML格式<key>value</key>
在這個例子中,C++程序經過調用lua_pushstring把C字符串壓入棧頂,lua_setglobal的做用是把棧頂的數據傳到Lua環境中做爲全局變量。
執行代碼完成後,使用lua_getglobal從Lua環境中取得全局變量壓入棧頂,而後使用lua_tostring把棧頂的數據轉成字符串。因爲lua_tostring自己沒有出棧功能,因此爲了平衡(即調用前與調用後棧裏的數據量不變),使用lua_pop彈出由lua_setglobal壓入的數據。
從上面的例子能夠看出,C++和Lua之間一直圍繞着棧在轉,可見棧是極爲重要的。有必要列出一些Lua C API中的主要棧操做先,它們的做用直接能夠從函數名中看出。
壓入元素到棧裏
void lua_pushnil (lua_State *L);
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s, size_t length);
void lua_pushstring (lua_State *L, const char *s);
void lua_pushcfunction (lua_State *L, lua_CFunction fn);
查詢棧裏的元素
lua_isnil (lua_State *L, int index);
lua_isboolean (lua_State *L, int index);
int lua_isnumber (lua_State *L, int index);
int lua_isstring (lua_State *L, int index);
int lua_isfunction (lua_State *L, int index);
int lua_istable (lua_State *L, int index);
int lua_isuserdata (lua_State *L, int index);
lua_islightuserdata (lua_State *L, int index);
lua_isthread (lua_State *L, int index);
轉換棧裏的元素
int lua_toboolean (lua_State *L, int index);
double lua_tonumber (lua_State *L, int index);
const char * lua_tostring (lua_State *L, int index);
const char * lua_tolstring (lua_State *L, int idx, size_t *len);
size_t lua_strlen (lua_State *L, int index);
lua_CFunction lua_tocfunction (lua_State *L, int idx);
void * lua_touserdata (lua_State *L, int idx);
lua_State * lua_tothread (lua_State *L, int idx);
Lua棧的維護
int lua_gettop (lua_State *L);
取得棧頂元素的索引,即棧中元素的個數
void lua_settop (lua_State *L, int index);
設置棧頂索引,即設置棧中元素的個數,若是index<0,則從棧頂往下數,下同
void lua_pushvalue (lua_State *L, int index);
把棧中指定索引的元素複製一份到棧頂
void lua_remove (lua_State *L, int index);
刪除指定索引的元素
void lua_insert (lua_State *L, int index);
移動棧頂元素到指定索引的位置,棧中數目沒有改變
void lua_replace (lua_State *L, int index);
從棧頂彈出元素值並將其設置到指定索引位置,棧中的數目減一
int lua_checkstack (lua_State *L, int extra);
確保堆棧上至少有 extra 個空位。若是不能把堆棧擴展到相應的尺寸,函數返回 false 。這個函數永遠不會縮小堆棧。
int lua_pop(L,n)
從棧頂彈出n個元素,它是一個lua_settop的包裝:#define lua_pop(L,n) lua_settop(L, -(n)-1)
表的操做
上面的列表中並無lua_pushtable和lua_totable,那麼怎樣取得或設置Lua中的table數據呢?
在Lua中,table是一個很重要的數據類型,在table中不只能夠象C中的數據同樣放一組數據,還能夠象map同樣以key=value的方式存放數據,如Lua代碼中的:
tb = {"abc",12,true,x=10,y=20,z=30}
前三個數據能夠用tb[1]~tb[3]取得
然後三個數據經過tb.x, tb.y, tb.z取得
儘管看起來很牛叉,不過剝開神奇的外衣,實際上Lua的table中,全部的數據都是以key=value的形式存放的,這句Lua代碼也能夠寫成:
tb = {[1]="abc", [2]=12, [3] = true, ["x"]=10, ["y"]=20, ["z"]=30}
它的形式就是[key]=value,所謂的tb.x只是tb["x"]的語法糖而已,若是願意,也能夠用tb["x"]取得這個數據10。
咱們把上面的例子改爲使用表的
1. ...
2. int main()
3. {
4. //Lua示例代碼,使用table
5. char *szLua_code =
6. "x = {} --用於存放結果的table "
7. "x[1],x[2] = string.gsub(c.Str, c.Mode, c.Tag) --x[1]裏是結果,x[2]裏是替換次數 "
8. "x.u = string.upper(x[1])";
9. //Lua的字符串模式
10. char *szMode = "(%w+)%s*=%s*(%w+)";
11. //要處理的字符串
12. char *szStr = "key1 = value1 key2 = value2";
13. //目標字符串模式
14. char *szTag = "<%1>%2</%1>";
15.
16. lua_State *L = luaL_newstate();
17. luaL_openlibs(L);
18.
19. //把一個tabele送給Lua
20. lua_newtable(L); //新建一個table並壓入棧頂
21. lua_pushstring(L, "Mode");// key
22. lua_pushstring(L, szMode);// value
23. //設置newtable[Mode]=szMode
24. //因爲上面兩次壓棧,如今table元素排在棧頂往下數第三的位置
25. lua_settable(L, -3);
26. //lua_settable會本身彈出上面壓入的key和value
27.
28. lua_pushstring(L, "Tag");// key
29. lua_pushstring(L, szTag);// value
30. lua_settable(L, -3); //設置newtable[Tag]=szTag
31.
32. lua_pushstring(L, "Str");// key
33. lua_pushstring(L, szStr);// value
34. lua_settable(L, -3); //設置newtable[Str]=szStr
35.
36. lua_setglobal(L,"c"); //將棧頂元素(newtable)置爲Lua中的全局變量c
37.
38. //執行
39. bool err = luaL_loadbuffer(L, szLua_code, strlen(szLua_code),
40. "demo") || lua_pcall(L, 0, 0, 0);
41. if(err)
42. {
43. //若是錯誤,顯示
44. cerr << lua_tostring(L, -1);
45. //彈出棧頂的這個錯誤信息
46. lua_pop(L, 1);
47. }
48. else
49. {
50. //Lua執行後取得全局變量的值
51. lua_getglobal(L, "x");
52.
53. //這個x應該是個table
54. if(lua_istable(L,-1))
55. {
56. //取得x.u,即x["u"]
57. lua_pushstring(L,"u"); //key
58. //因爲此次壓棧,x處於棧頂第二位置
59. lua_gettable(L,-2);
60. //lua_gettable會彈出上面壓入的key,而後把對應的value壓入
61. //取得數據,而後從棧中彈出這個value
62. cout << "x.u = " << lua_tostring(L,-1) << endl;
63. lua_pop(L, 1);
64.
65. //取得x[1]和x[2]
66. for(int i=1; i<=2; i++)
67. {
68. //除了key是數字外,與上面的沒什麼區別
69. lua_pushnumber(L,i);
70. lua_gettable(L,-2);
71. cout << "x[" << i <<"] = " << lua_tostring(L,-1) << endl;
72. lua_pop(L, 1);
73. }
74. }
75.
76. //彈出棧頂的x
77. lua_pop(L, 1);
78. }
79. lua_close(L);
80. return 0;
81.}
本例中用到的新Lua C API是:
void lua_newtable (lua_State *L);
新建一個空的table並壓入棧頂。
void lua_settable (lua_State *L, int idx);
lua_settable以table在棧中的索引做爲參數,並將棧頂的key和value出棧,用這兩個值修改table。
void lua_gettable (lua_State *L, int idx);
lua_gettable以table在棧中的索引做爲參數,彈出棧頂的元素做爲key,返回與key對應的value並壓入棧頂。
最後,Lua告別針對table提供了存取函數
void lua_rawgeti (lua_State *L, int idx, int n)
取得table[n]並放到棧頂,上例中69-70行的lua_pushnumber(L,i);lua_gettable(L,-2);能夠用lua_rawgeti(L,-1)代替。
lua_getfield (lua_State *L, int idx, const char *k)
取得table.k並放到棧頂,上例中57-59行的lua_pushstring(L,"u");lua_gettable(L,-2);能夠替換成lua_getfield(L,-1,"u")。
void lua_setfield (lua_State *L, int idx, const char *k)
把棧頂的數據做爲value放入table.k中,上例中的形如lua_pushstring(L, "key");lua_pushstring(L, value);lua_settable(L, -3);能夠改爲lua_pushstring(L, value);lua_setfield(L,-2,"key");的形式。
void lua_rawseti (lua_State *L, int idx, int n)
把棧頂的數據做爲value放入table[n]中
例三,在C++中調用Lua子函數
在Lua中,函數和boolean同樣也屬於基本數據類型,因此一樣可使用lua_getglobal來取得函數,剩下的問題只是怎樣執行它(函數元素)的問題了。
1. ...
2. int main()
3. {
4. //Lua示例代碼,是一個函數
5. char *szLua_code =
6. "function gsub(Str, Mode, Tag)"
7. " a,b = string.gsub(Str, Mode, Tag) "
8. " c = string.upper(a) "
9. " return a,b,c --多個返回值 "
10. "end";
11. //Lua的字符串模式
12. char *szMode = "(%w+)%s*=%s*(%w+)";
13. //要處理的字符串
14. char *szStr = "key1 = value1 key2 = value2";
15. //目標字符串模式
16. char *szTag = "<%1>%2</%1>";
17.
18. lua_State *L = luaL_newstate();
19. luaL_openlibs(L);
20.
21. //執行
22. bool err = luaL_loadbuffer(L, szLua_code, strlen(szLua_code),
23. "demo") || lua_pcall(L, 0, 0, 0);
24. if(err)
25. {
26. cerr << lua_tostring(L, -1);
27. lua_pop(L, 1);
28. }
29. else
30. {
31. //Lua執行後取得全局變量的值
32. lua_getglobal(L, "gsub");
33. if(lua_isfunction(L,-1)) //確認一下是個函數
34. {
35. //依次放入三個參數
36. lua_pushstring(L,szStr);
37. lua_pushstring(L,szMode);
38. lua_pushstring(L,szTag);
39. //調用,咱們有3個參數,要獲得2個結果
40. //你可能注意到gsub函數返回了3個,不過咱們只要2個,這沒有問題
41. //沒有使用錯誤處理回調,因此lua_pcall最後一個參數是0
42. if(0 != lua_pcall(L, 3, 2, 0))
43. {
44. //若是錯誤,顯示
45. cerr << lua_tostring(L, -1);
46. lua_pop(L, 1);
47. }
48. else
49. {
50. //正確,獲得兩個結果,注意在棧裏的順序
51. cout << "a = " << lua_tostring(L, -2) << endl;
52. cout << "b = " << lua_tostring(L, -1) << endl;
53. //彈出這兩個結果
54. lua_pop(L, 2);
55. }
56. }
57. else
58. {
59. lua_pop(L,1);
60. }
61. }
62. lua_close(L);
63. return 0;
64.}
調用Lua子函數使用的是lua_pcall函數,咱們的全部例子中都有這個函數,它的說明以下:
lua_pcall (lua_State *L, int nargs, int nresults, int errfunc);
做用:以保護模式調用一個函數。
要調用一個函數請遵循如下協議:首先,要調用的函數應該被壓入堆棧;接着,把須要傳遞給這個函數的參數按正序壓棧;這是指第一個參數首先壓棧。最後調用lua_pcall;
nargs 是你壓入堆棧的參數個數。當函數調用完畢後,全部的參數以及函數自己都會出棧。而函數的返回值這時則被壓入堆棧。返回值的個數將被調整爲 nresults 個,除非 nresults 被設置成 LUA_MULTRET。在這種狀況下,全部的返回值都被壓入堆棧中。 Lua 會保證返回值都放入棧空間中。函數返回值將按正序壓棧(第一個返回值首先壓棧),所以在調用結束後,最後一個返回值將被放在棧頂。
若是有錯誤發生的話, lua_pcall 會捕獲它,而後把單一的值(錯誤信息)壓入堆棧,而後返回錯誤碼。lua_pcall 老是把函數自己和它的參數從棧上移除。
若是 errfunc 是 0 ,返回在棧頂的錯誤信息就和原始錯誤信息徹底一致。不然,這個函數會被調用而參數就是錯誤信息。錯誤處理函數的返回值將被 lua_pcall 做爲出錯信息返回在堆棧上。
閉包和僞索引
http://www.cppprog.com/2009/0210/63.html
例四,在Lua代碼中調用C++函數
能Lua代碼中調用C函數對Lua來講相當重要,讓Lua能真正站到C這個巨人的肩膀上。
要寫一個能讓Lua調用的C函數,就要符合lua_CFunction定義:typedef int (*lua_CFunction) (lua_State *L);
當Lua調用C函數的時候,一樣使用棧來交互。C函數從棧中獲取她的參數,調用結束後將結果放到棧中,並返回放到棧中的結果個數。
這兒有一個重要的概念:用來交互的棧不是全局棧,每個函數都有他本身的私有棧。當Lua調用C函數的時候,第一個參數老是在這個私有棧的index=1的位置。
1. ...
2. #include <complex> //複數
3.
4. //C函數,作複數計算,輸入實部,虛部。輸出絕對值和角度
5. int calcComplex(lua_State *L)
6. {
7. //從棧中讀入實部,虛部
8. double r = luaL_checknumber(L,1);
9. double i = luaL_checknumber(L,2);
10. complex<double> c(r,i);
11. //存入絕對值
12. lua_pushnumber(L,abs(c));
13. //存入角度
14. lua_pushnumber(L,arg(c)*180.0/3.14159);
15. return 2;//兩個結果
16.}
17.
18.int main()
19.{
20. char *szLua_code =
21. "v,a = CalcComplex(3,4) "
22. "print(v,a)";
23.
24. lua_State *L = luaL_newstate();
25. luaL_openlibs(L);
26.
27. //放入C函數
28. lua_pushcfunction(L, calcComplex);
29. lua_setglobal(L, "CalcComplex");
30.
31. //執行
32. bool err = luaL_loadstring(L, szLua_code) || lua_pcall(L, 0, 0, 0);
33. if(err)
34. {
35. cerr << lua_tostring(L, -1);
36. lua_pop(L, 1);
37. }
38.
39. lua_close(L);
40. return 0;
41.}
結果返回5 53.13...,和其它數據同樣,給Lua代碼提供C函數也是經過棧來操做的,由於lua_pushcfunction和lua_setglobal的 組合很經常使用,因此Lua提供了一個宏:
#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n)))
這兩句代碼也就可寫成lua_register(L,"CalcComplex",calcComplex);
閉包(closure)
在編寫用於Lua的C函數時,咱們可能須要一些相似於面向對象的能力,好比咱們想在Lua中使用象這樣的一個計數器類:
1. struct CCounter{
2. CCounter()
3. :m_(0){}
4. int count(){
5. return ++i;
6. }
7. private:
8. int m_;
9. };
這裏若是咱們僅僅使用lua_pushcfunction提供一個count函數已經不能知足要求(使用static? 不行,這樣就不能同時使用多個計數器,而且若是程序中有多個Lua環境的話它也不能工做)。
這時咱們就須要一種機制讓數據與某個函數關聯,造成一個總體,這就是Lua中的閉包,而閉包裏與函數關聯的數據稱爲UpValue。
使用Lua閉包的方法是定義一個工廠函數,由它來指定UpValue的初值和對應的函數,如:
1. ...
2. //計算函數
3. int count(lua_State *L)
4. {
5. //獲得UpValue
6. double m_ = lua_tonumber(L, lua_upvalueindex(1));
7. //更改UpValue
8. lua_pushnumber(L, ++m_);
9. lua_replace(L, lua_upvalueindex(1));
10. //返回結果(直接複製一份UpValue做爲結果)
11. lua_pushvalue(L, lua_upvalueindex(1));
12. return 1;
13.}
14.//工廠函數,把一個數字和count函數關聯打包後返回閉包。
15.int newCount(lua_State *L)
16.{
17. //計數器初值(即UpValue)
18. lua_pushnumber(L,0);
19. //放入計算函數,告訴它與這個函數相關聯的數據個數
20. lua_pushcclosure(L, count, 1);
21. return 1;//一個結果,即函數體
22.}
23.
24.int main()
25.{
26. char *szLua_code =
27. "c1 = NewCount() "
28. "c2 = NewCount() "
29. "for i=1,5 do print(c1()) end "
30. "for i=1,5 do print(c2()) end";
31.
32. lua_State *L = luaL_newstate();
33. luaL_openlibs(L);
34.
35. //放入C函數
36. lua_register(L,"NewCount",newCount);
37.
38. //執行
39. bool err = luaL_loadstring(L, szLua_code) || lua_pcall(L, 0, 0, 0);
40. if(err)
41. {
42. cerr << lua_tostring(L, -1);
43. lua_pop(L, 1);
44. }
45.
46. lua_close(L);
47. return 0;
48.}
執行結果是:
1
2
3
4
5
1
2
3
4
5
能夠發現這兩個計算器之間沒有干擾,咱們成功地在Lua中生成了兩個「計數器類」。
這裏的關鍵函數是lua_pushcclosure,她的第二個參數是一個基本函數(例子中是count),第三個參數是UpValue的個數(例子中爲 1)。在建立新的閉包以前,咱們必須將關聯數據的初始值入棧,在上面的例子中,咱們將數字0做爲初始值入棧。如預期的同樣, lua_pushcclosure將新的閉包放到棧內,所以閉包做爲newCounter的結果被返回。
實際上,咱們以前使用的lua_pushcfunction只是lua_pushcclosure的一個特例:沒有UpValue的閉包。查看它的聲明可 以知道它只是一個宏而已:
#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0)
在count函數中,經過lua_upvalueindex(i)獲得當前閉包的UpValue所在的索引位置,查看它的定義能夠發現它只是一個簡單的 宏:
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
宏裏的LUA_GLOBALSINDEX是一個僞索引,關於僞索引的知識請看下節
僞索引
僞索引除了它對應的值不在棧中以外,其餘都相似於棧中的索引。Lua C API中大部分接受索引做爲參數的函數,也均可以接受假索引做爲參數。
僞索引被用來訪問線程的環境,函數的環境,Lua註冊表,還有C函數的UpValue。
- 線程的環境(也就是放全局變量的地方)一般在僞索引 LUA_GLOBALSINDEX 處。
- 正在運行的 C 函數的環境則放在僞索引 LUA_ENVIRONINDEX 之處。
- LUA_REGISTRYINDEX則存放着Lua註冊表。
- C函數UpValue的存放位置見上節。
這裏要重點講的是LUA_GLOBALSINDEX和LUA_REGISTRYINDEX,這兩個僞索引處的數據是table類型的。
LUA_GLOBALSINDEX位置上的table存放着全部的全局變量,好比這句
lua_getfield(L, LUA_GLOBALSINDEX, varname);
就是取得名爲varname的全局變量,咱們以前一直使用的lua_getglobal就是這樣定義的:#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
下面的代碼利用LUA_GLOBALSINDEX獲得全部的全局變量
1. int main()
2. {
3. char *szLua_code =
4. "a=10 "
5. "b=\"hello\" "
6. "c=true";
7.
8. lua_State *L = luaL_newstate();
9. luaL_openlibs(L);
10.
11. //執行
12. bool err = luaL_loadstring(L, szLua_code) || lua_pcall(L, 0, 0, 0);
13. if(err)
14. {
15. cerr << lua_tostring(L, -1);
16. lua_pop(L, 1);
17. }
18. else
19. {
20. //遍歷LUA_GLOBALSINDEX所在的table獲得
21. lua_pushnil(L);
22. while(0 != lua_next(L,LUA_GLOBALSINDEX))
23. {
24. // 'key' (在索引 -2 處) 和 'value' (在索引 -1 處)
25. /*
26. 在遍歷一張表的時候,不要直接對 key 調用 lua_tolstring ,
27. 除非你知道這個 key 必定是一個字符串。
28. 調用 lua_tolstring 有可能改變給定索引位置的值;
29. 這會對下一次調用 lua_next 形成影響。
30. 因此複製一個key到棧頂先
31. */
32. lua_pushvalue(L, -2);
33. printf("%s - %s ",
34. lua_tostring(L, -1), //key,剛纔複製的
35. lua_typename(L, lua_type(L,-2))); //value,如今排在-2的位置了
36. // 移除 'value' 和複製的key;保留源 'key' 作下一次疊代
37. lua_pop(L, 2);
38. }
39. }
40. lua_close(L);
41. return 0;
42.}
LUA_REGISTRYINDEX僞索引處也存放着一個table,它就是Lua註冊表(registry)。這個註冊表能夠用來保存任何C代碼想保存 的Lua值。
加入到註冊表裏的數據至關於全局變量,不過只有C代碼能夠存取而Lua代碼不能。所以用它來存儲函數庫(在下一節介紹)中的一些公共變量再好不過了。
一個Lua庫其實是一個定義了一系列Lua函數的代碼塊,並將這些函數保存在適當的地方,一般做爲table的域來保存。Lua的C庫就是這樣實現的。
做爲一個完整的庫,咱們還須要寫一個函數來負責把庫中的全部公共函數放到table裏,而後註冊到Lua全局變量裏,就像luaopen_*作的同樣。 Lua爲這種需求提供了輔助函數luaL_register,它接受一個C函數的列表和他們對應的函數名,而且做爲一個庫在一個table中註冊全部這些函數。
下例中註冊了一個名爲Files的庫,定義了三個庫函數:FindFirst,FindNext,FindClose。
1. extern "C" {
2. #include "lua.h"
3. #include "lualib.h"
4. #include "lauxlib.h"
5. }
6.
7. #include <iostream>
8. #include <string>
9. #include <windows.h>
10.using namespace std;
11.
12.//函數庫示例,Windows下查找文件功能
13.//輸入:string路徑名
14.//輸出:userdata存放Handle(若是沒找到,則是nil), string文件名
15.int findfirst( lua_State *L )
16.{
17. WIN32_FIND_DATAA FindFileData;
18. HANDLE hFind = ::FindFirstFileA(luaL_checkstring(L,1), &FindFileData);
19.
20. if(INVALID_HANDLE_VALUE == hFind)
21. lua_pushnil(L);
22. else
23. lua_pushlightuserdata(L, hFind);
24.
25. lua_pushstring(L, FindFileData.cFileName);
26.
27. return 2;
28.}
29.
30.//輸入:userdata:findfirst返回的Handle
31.//輸出:string:文件名,若是沒找到,則返回nil
32.int findnext( lua_State *L )
33.{
34. WIN32_FIND_DATAA FindFileData;
35. if(::FindNextFileA(lua_touserdata(L,1),&FindFileData))
36. lua_pushstring(L, FindFileData.cFileName);
37. else
38. lua_pushnil(L);
39. return 1;
40.}
41.
42.//輸入:userdata:findfirst返回的Handle
43.//沒有輸出
44.int findclose( lua_State *L )
45.{
46. ::FindClose(lua_touserdata(L,1));
47. return 0;
48.}
49.
50.//註冊函數庫
51.static const struct luaL_reg lrFiles [] = {
52. {"FindFirst", findfirst},
53. {"FindNext", findnext},
54. {"FindClose", findclose},
55. {NULL, NULL} /* sentinel */
56.};
57.int luaopen_Files (lua_State *L) {
58. luaL_register(L, "Files", lrFiles);
59. return 1;
60.}
61.
62.int main()
63.{
64. char* szLua_code=
65. "hFind,sFile = Files.FindFirst('c:\\\\*.*'); "
66. "if hFind then "
67. " repeat "
68. " print(sFile) "
69. " sFile = Files.FindNext(hFind) "
70. " until sFile==nil; "
71. " Files.FindClose(hFind) "
72. "end";
73. lua_State *L = luaL_newstate();
74. luaL_openlibs(L);
75. luaopen_Files(L);
76.
77. bool err = luaL_loadstring(L, szLua_code) || lua_pcall(L, 0, 0, 0);
78. if(err)
79. {
80. cerr << lua_tostring(L, -1);
81. lua_pop(L, 1);
82. }
83. lua_close(L);
84. return 0;
85.}
本例運行結果是顯示出C盤根目錄下全部的文件名。
Lua官方建議把函數庫寫進動態連接庫中(windows下.dll文件,linux下.so文件),這樣就能夠在Lua代碼中使用loadlib函數動 態載入函數庫
例如,咱們把上面的例子改爲動態連接庫版本:
DLL代碼,假定生成的文件名爲fileslib.dll:
1. extern "C" {
2. #include "lua.h"
3. #include "lualib.h"
4. #include "lauxlib.h"
5. }
6. #include <windows.h>
7.
8. BOOL APIENTRY DllMain( HMODULE hModule,
9. DWORD ul_reason_for_call,
10. LPVOID lpReserved
11. )
12.{
13. return TRUE;
14.}
15.
16.//函數庫示例,Windows下查找文件功能
17.//輸入:string路徑名
18.//輸出:userdata存放Handle(若是沒找到,則是nil), string文件名
19.int findfirst( lua_State *L )
20.{
21. WIN32_FIND_DATAA FindFileData;
22. HANDLE hFind = ::FindFirstFileA(luaL_checkstring(L,1), &FindFileData);
23.
24. if(INVALID_HANDLE_VALUE == hFind)
25. lua_pushnil(L);
26. else
27. lua_pushlightuserdata(L, hFind);
28.
29. lua_pushstring(L, FindFileData.cFileName);
30.
31. return 2;
32.}
33.
34.//輸入:userdata:findfirst返回的Handle
35.//輸出:string:文件名,若是沒找到,則返回nil
36.int findnext( lua_State *L )
37.{
38. WIN32_FIND_DATAA FindFileData;
39. if(::FindNextFileA(lua_touserdata(L,1),&FindFileData))
40. lua_pushstring(L, FindFileData.cFileName);
41. else
42. lua_pushnil(L);
43. return 1;
44.}
45.
46.//輸入:userdata:findfirst返回的Handle
47.//沒有輸出
48.int findclose( lua_State *L )
49.{
50. ::FindClose(lua_touserdata(L,1));
51. return 0;
52.}
53.
54.//註冊函數庫
55.static const struct luaL_reg lrFiles [] = {
56. {"FindFirst", findfirst},
57. {"FindNext", findnext},
58. {"FindClose", findclose},
59. {NULL, NULL} /* sentinel */
60.};
61.//導出,注意原型爲typedef int (*lua_CFunction) (lua_State *L);
62.extern "C" __declspec(dllexport) int luaopen_Files (lua_State *L) {
63. luaL_register(L, "Files", lrFiles);
64. return 1;
65.}
Lua調用代碼(或者直接使用Lua.exe調用,dll文件必須處於package.cpath指定的目錄中,默認與執行文件在同一目錄便可):
1. extern "C" {
2. #include "lua.h"
3. #include "lualib.h"
4. #include "lauxlib.h"
5. }
6.
7. #include <iostream>
8. #include <string>
9. #include <windows.h>
10.using namespace std;
11.
12.int main()
13.{
14. char* szLua_code=
15. "fileslib = package.loadlib('fileslib.dll', 'luaopen_Files') "
16. "fileslib() "
17. "hFind,sFile = Files.FindFirst('c:\\\\*.*'); "
18. "if hFind then "
19. " repeat "
20. " print(sFile) "
21. " sFile = Files.FindNext(hFind) "
22. " until sFile==nil; "
23. " Files.FindClose(hFind) "
24. "end";
25. lua_State *L = luaL_newstate();
26. luaL_openlibs(L);
27.
28. bool err = luaL_loadstring(L, szLua_code) || lua_pcall(L, 0, 0, 0);
29. if(err)
30. {
31. cerr << lua_tostring(L, -1);
32. lua_pop(L, 1);
33. }
34. lua_close(L);
35. return 0;
36.}
Lua代碼裏使用package.loadlib獲得動態連接庫中的luaopen_Files函數,而後調用它註冊到Lua中,若是動態連接庫中的導出 函數名稱知足luaopen_<庫名>的話,還可使用require直接載入。
好比,若是把本例中的DLL代碼裏的導出函數名luaopen_Files改爲luaopen_fileslib的話,Lua代碼即可以改爲:
1. char* szLua_code=
2. "require('fileslib') "
3. "hFind,sFile = Files.FindFirst('c:\\\\*.*'); "
4. ...
例五,與Lua交換自定義數據
因爲Lua中的數據類型遠不能知足C語言的須要,爲此Lua提供了userdata,一個userdata提供了一個在Lua中沒有預約義操做的raw內 存區域。
在例四的函數庫代碼中咱們已經使用過lightuserdata,它是userdata的一個特例:一個表示C指針的值(也就是一個void *類型的值)。
下面的例子咱們使用userdata來給Lua提供一個窗體類用於創建,顯示窗體。爲了簡化窗體控制代碼,在C函數中咱們使用了C++Builder的 VCL庫,因此下面的代碼要在C++Builder下編譯才能經過。固然,稍微修改一下也可使用MFC,QT,wxWidget等來代替。
1. //---------------------------------------------------------------------------
2. #include <vcl.h>
3. extern "C" {
4. #include "lua.h"
5. #include "lualib.h"
6. #include "lauxlib.h"
7. }
8.
9. #include <iostream>
10.#pragma hdrstop
11.//---------------------------------------------------------------------------
12.#pragma argsused
13.
14.typedef TWinControl* PWinControl;
15.//建立窗體,輸入父窗體(或nil),類型,標題
16.//輸出建立後的窗體
17.int newCtrl(lua_State *L)
18.{
19. //input:TWinControl *Parent, type(TForm,TButton,TEdit), text(optional)
20. TWinControl *Parent = NULL;
21. //從userdata中取得TWinControl*
22. if(lua_isuserdata(L,1))
23. Parent = *(PWinControl*)lua_touserdata(L,1);
24. String Type = UpperCase(luaL_checkstring(L, 2));
25. String Text = lua_tostring(L, 3);
26.
27. TWinControl *R = NULL;
28.
29. if(Type == "FORM")
30. {
31. R = new TForm(Application);
32. }
33. else if(Type == "BUTTON")
34. {
35. R = new TButton(Application);
36. }
37. else if(Type == "EDIT")
38. {
39. R = new TEdit(Application);
40. }
41. else
42. {
43. luaL_error(L, "unknow type!");
44. }
45.
46. if(Parent)
47. R->Parent = Parent;
48.
49. if(!Text.IsEmpty())
50. ::SetWindowText(R->Handle, Text.c_str());
51.
52. //新建userdata,大小爲sizeof(PWinControl),用於存放上面生成的窗體指針
53. PWinControl* pCtrl = (PWinControl*)lua_newuserdata(L,sizeof(PWinControl));
54. *pCtrl = R;
55. return 1;
56.}
57.
58.//顯示窗體
59.int showCtrl(lua_State *L)
60.{
61. //input: TWinControl*, for TForm, use ShowModal
62. TWinControl* Ctrl = *(PWinControl*)lua_touserdata(L,1);
63. TForm *fm = dynamic_cast<TForm*>(Ctrl);
64. if(fm)
65. fm->ShowModal();
66. else
67. Ctrl->Show();
68. return 0;
69.}
70.
71.//定位窗體,輸入窗體,左,上,右,下
72.int posCtrl(lua_State *L)
73.{
74. //input: TWinControl*, Left, Top, Right, Bottom
75. TWinControl* Ctrl = *(PWinControl*)lua_touserdata(L,1);
76. Ctrl->BoundsRect = TRect(
77. luaL_checkint(L, 2),
78. luaL_checkint(L, 3),
79. luaL_checkint(L, 4),
80. luaL_checkint(L, 5));
81.
82. return 0;
83.}
84.
85.//刪除窗體
86.int delCtrl(lua_State *L)
87.{
88. //input: TWinControl*
89. TWinControl* Ctrl = *(PWinControl*)lua_touserdata(L,1);
90. delete Ctrl;
91. return 0;
92.}
93.
94.//把這些函數做爲VCL函數庫提供給Lua
95.static const struct luaL_reg lib_VCL [] = {
96. {"new", newCtrl},
97. {"del", delCtrl},
98. {"pos", posCtrl},
99. {"show", showCtrl},
100. {NULL, NULL}
101. };
102.
103. int luaopen_VCL (lua_State *L) {
104. luaL_register(L, "VCL", lib_VCL);
105. return 1;
106. }
107.
108. int main(int argc, char* argv[])
109. {
110. char* szLua_code=
111. "fm = VCL.new(nil,'Form','Lua Demo'); " //新建主窗體fm
112. "VCL.pos(fm, 200, 200, 500, 300); " //定位
113. "edt = VCL.new(fm, 'Edit', 'Hello World'); " //在fm上創建一個編輯框edt
114. "VCL.pos(edt, 5, 5, 280, 28); "
115. "btn = VCL.new(fm, 'Button', 'Haha'); " //在fm上創建一個按鈕btn
116. "VCL.pos(btn, 100, 40, 150, 63); "
117. "VCL.show(edt); "
118. "VCL.show(btn); "
119. "VCL.show(fm); " //顯示
120. "VCL.del(fm);"; //刪除
121.
122. lua_State *L = luaL_newstate();
123. luaL_openlibs(L);
124. luaopen_VCL(L);
125.
126. bool err = luaL_loadstring(L, szLua_code) || lua_pcall(L, 0, 0, 0);
127. if(err)
128. {
129. std::cerr << lua_tostring(L, -1);
130. lua_pop(L, 1);
131. }
132.
133. lua_close(L);
134. return 0;
135. }
136. //---------------------------------------------------------------------------
使用metatable提供面向對象調用方式
上面的VCL代碼庫爲Lua提供了GUI的支持,可是看那些Lua代碼,還處於面向過程時期。如何能把VCL.show(edt)之類的代碼改爲edt: show()這樣的形式呢?仍是先看代碼:
1. //---------------------------------------------------------------------------
2. #include <vcl.h>
3. extern "C" {
4. #include "lua.h"
5. #include "lualib.h"
6. #include "lauxlib.h"
7. }
8.
9. #include <iostream>
10.#pragma hdrstop
11.//---------------------------------------------------------------------------
12.#pragma argsused
13.
14.typedef TWinControl* PWinControl;
15.//建立窗體,輸入父窗體(或nil),類型,標題
16.//輸出建立後的窗體
17.int newCtrl(lua_State *L)
18.{
19. //input:TWinControl *Parent, type(TForm,TButton,TEdit), text(optional)
20. TWinControl *Parent = NULL;
21.
22. if(lua_isuserdata(L,1))
23. Parent = *(PWinControl*)luaL_checkudata(L,1,"My_VCL");
24. String Type = UpperCase(luaL_checkstring(L, 2));
25. String Text = lua_tostring(L, 3);
26.
27. TWinControl *R = NULL;
28.
29. if(Type == "FORM")
30. R = new TForm(Application);
31. else if(Type == "BUTTON")
32. R = new TButton(Application);
33. else if(Type == "EDIT")
34. R = new TEdit(Application);
35. else
36. luaL_error(L, "unknow type!");
37.
38. if(Parent)
39. R->Parent = Parent;
40.
41. if(!Text.IsEmpty())
42. ::SetWindowText(R->Handle, Text.c_str());
43.
44. //output TWinControl*
45. PWinControl* pCtrl = (PWinControl*)lua_newuserdata(L,sizeof(PWinControl));
46. *pCtrl = R;
47. //關聯metatable
48. luaL_getmetatable(L, "My_VCL");
49. lua_setmetatable(L, -2);
50. return 1;
51.}
52.
53.//顯示窗體
54.int showCtrl(lua_State *L)
55.{
56. //input: TWinControl*, for TForm, use ShowModal
57. TWinControl* Ctrl = *(PWinControl*)luaL_checkudata(L,1,"My_VCL");
58. TForm *fm = dynamic_cast<TForm*>(Ctrl);
59. if(fm)
60. fm->ShowModal();
61. else
62. Ctrl->Show();
63. return 0;
64.}
65.
66.//定位窗體,輸入窗體,左,上,右,下
67.int posCtrl(lua_State *L)
68.{
69. //input: TWinControl*, Left, Top, Right, Bottom
70. TWinControl* Ctrl = *(PWinControl*)luaL_checkudata(L,1,"My_VCL");
71. Ctrl->BoundsRect = TRect(
72. luaL_checkint(L, 2),
73. luaL_checkint(L, 3),
74. luaL_checkint(L, 4),
75. luaL_checkint(L, 5));
76.
77. return 0;
78.}
79.
80.//刪除窗體
81.int delCtrl(lua_State *L)
82.{
83. //input: TWinControl*
84. TWinControl* Ctrl = *(PWinControl*)luaL_checkudata(L,1,"My_VCL");
85. delete Ctrl;
86. return 0;
87.}
88.
89.//把這些函數做爲VCL函數庫提供給Lua
90.static const struct luaL_reg lib_VCL [] = {
91. {"new", newCtrl},
92. {"del", delCtrl},
93. {"pos", posCtrl},
94. {"show", showCtrl},
95. {NULL, NULL}
96.};
97.
98.int luaopen_VCL (lua_State *L) {
99. //創建metatable
100. luaL_newmetatable(L, "My_VCL");
101.
102. //查找索引,把它指向metatable自身(由於稍後咱們會在metatable里加入一些成員)
103. lua_pushvalue(L, -1);
104. lua_setfield(L,-2,"__index");
105.
106. //pos方法
107. lua_pushcfunction(L, posCtrl);
108. lua_setfield(L,-2,"pos");
109.
110. //show方法
111. lua_pushcfunction(L, showCtrl);
112. lua_setfield(L,-2,"show");
113.
114. //析構,若是表裏有__gc,Lua的垃圾回收機制會調用它。
115. lua_pushcfunction(L, delCtrl);
116. lua_setfield(L,-2,"__gc");
117.
118. luaL_register(L, "VCL", lib_VCL);
119. return 1;
120. }
121.
122. int main(int argc, char* argv[])
123. {
124. char* szLua_code=
125. "local fm = VCL.new(nil,'Form','Lua Demo'); " //新建主窗體fm
126. "fm:pos(200, 200, 500, 300); " //定位
127. "local edt = VCL.new(fm, 'Edit', 'Hello World'); " //在fm上創建一個編輯框edt
128. "edt:pos(5, 5, 280, 28); "
129. "local btn = VCL.new(fm, 'Button', 'Haha'); " //在fm上創建一個按鈕btn
130. "btn:pos(100, 40, 150, 63); "
131. "edt:show(); "
132. "btn:show(); "
133. "fm:show(); "; //顯示
134. //"VCL.del(fm);"; //再也不須要刪除了,Lua的垃圾回收在回收userdata地會調用metatable.__gc。
135.
136. lua_State *L = luaL_newstate();
137. luaL_openlibs(L);
138. luaopen_VCL(L);
139.
140. bool err = luaL_loadstring(L, szLua_code) || lua_pcall(L, 0, 0, 0);
141. if(err)
142. {
143. std::cerr << lua_tostring(L, -1);
144. lua_pop(L, 1);
145. }
146.
147. lua_close(L);
148. return 0;
149. }
150. //---------------------------------------------------------------------------
咱們這兒用到的輔助函數有:
int luaL_newmetatable (lua_State *L, const char *tname);
建立一個新表(用於metatable),將新表放到棧頂並在註冊表中創建一個類型名與之聯繫。
void luaL_getmetatable (lua_State *L, const char *tname);
獲取註冊表中tname對應的metatable。
int lua_setmetatable (lua_State *L, int objindex);
把一個table彈出堆棧,並將其設爲給定索引處的值的 metatable。
void *luaL_checkudata (lua_State *L, int index, const char *tname);
檢查在棧中指定位置的對象是否爲帶有給定名字的metatable的userdata。
咱們只改動了luaopen_VCL和newCtrl函數。
在luaopen_VCL裏,咱們創建了一個metatable,而後讓它的__index成員指向自身,並加入了pos,show函數成員和__gc函 數成員。
在newCtrl裏,咱們把luaopen_VCL裏創建的metatable和新建的userdata關聯,因而:
- 對userdata的索引操做就會轉向metatable.__index
- 由於metatable.__index是metatable自身,因此就在這個metatable裏查找
- 這樣,對userdata的pos、show索引轉到metatable裏的pos和show上,它們指向的是咱們的C函數posCtrl和 posShow。
- 最後,當Lua回收這些userdata前,會調用metatable.__gc(若是有的話),咱們已經把metatable.__gc指向了C函數 delCtrl。
加入metatable後,咱們還獲得了額外的好處:能夠區分不一樣的userdata以保證類型安全,咱們把因此的lua_touserdata改爲了luaL_checkudata。
關於metatable的知識已超出本文討論範圍,請參考Lua官方手冊。
在C++中使用Lua(三)
http://www.cppprog.com/2009/0211/64.html
例六,使用C++包裝類
儘管用Lua的C API已經能夠方便地寫出與Lua交互的程序了,不過對於用慣C++的人來講仍是更願意用C++的方式來解決問題。因而開源社區就出現了很多Lua C API的C++的wrap,好比:LuaBind,LuaPlus,toLua
這裏介紹的是LuaBind庫,下載
它在Windows下貌似只能支持MSVC和ICC,好在Lua能夠支持動態庫的載入,因此用VC+LuaBind寫Lua庫,再用C++Builder調用也是個好主意。
在VC使用LuaBind的方法是把LuaBind的src目錄下的cpp文件加入工程(固然也能夠先編譯成靜態庫),加入Lua庫,設置LuaBind,Lua和Boost的頭文件路徑。
頭文件:
1. //Lua頭文件
2. extern "C"
3. {
4. #include <lua.h>
5. #include <lualib.h>
6. #include <lauxlib.h>
7. }
8. //LuaBind頭文件
9. #include <luabind/luabind.hpp>
在C++中調用Lua函數
調用Lua函數那是最簡單不過的事情了,用LuaBind的call_function()模板函數就能夠了:
1. int main(
2. // 創建新的Lua環境
3. lua_State *myLuaState = luaL_newstate();
4.
5. // 讓LuaBind「認識」這個Lua環境
6. luabind::open(myLuaState);
7.
8. // 定義一個叫add的Lua函數
9. luaL_dostring(
10. myLuaState,
11. "function add(first, second) "
12. " return first + second "
13. "end "
14. );
15.
16. //調用add函數
17. cout << "Result: "
18. << luabind::call_function<int>(myLuaState, "add", 2, 3)
19. << endl;
20.
21. lua_close(myLuaState);
22.}
在本例中咱們先使用Lua C API產生一個Lua線程環境,而後調用luabind::open()讓LuaBind關聯這個線程環境,在使用LuaBind以前這步是必須作的,它要在Lua環境中註冊一些LuaBind專用的數據。
在執行完Lua代碼以後,咱們使用luabind::call_function<int>調用了Lua裏的add函數,返回值是int。
在Lua代碼中調用C++函數
從前面的文章裏咱們知道在Lua調用C函數須要常常操做棧,而LuaBind幫咱們作了這些工做,下面的例子把print_hello函數送給Lua腳本調用:
1. void print_hello(int number) {
2. cout << "hello world " << number << endl;
3. }
4.
5. int main(
6. // 創建新的Lua環境
7. lua_State *myLuaState = lua_open();
8.
9. // 讓LuaBind「認識」這個Lua環境
10. luabind::open(myLuaState);
11.
12. // 添加print_hello函數到Lua環境中
13. luabind::module(myLuaState) [
14. luabind::def("print_hello", print_hello)
15. ];
16.
17. // 如今Lua中能夠調用print_hello了
18. luaL_dostring(
19. myLuaState,
20. "print_hello(123) "
21. );
22.
23. lua_close(myLuaState);
24.}
向Lua環境加入函數或其它東東的方法是:
luabind::module(lua_State* L, char const* name = 0) [
...
];
其中module函數中的第二個指定要加入的東東所處的名空間(其實就是table),若是爲0,則處於全局域之下。
在中括號裏的luabind::def把print_hello函數提供給Lua環境,第一個參數是Lua中使用的函數名。
若是要定義多個函數,可使用逗號分隔。
在Lua代碼中使用C++類
若是咱們直接使用Lua C API向Lua腳本註冊一個C++類,通常是使用userdata+metatable的方法,就象咱們在例五中作的同樣。這樣作儘管難度不大,卻很是繁瑣並且不方便維護。
使用LuaBind咱們就能夠更方便地向Lua腳本註冊C++類了,例:
1. class NumberPrinter {
2. public:
3. NumberPrinter(int number) :
4. m_number(number) {}
5.
6. void print() {
7. cout << m_number << endl;
8. }
9.
10. private:
11. int m_number;
12.};
13.
14.int main() {
15. lua_State *myLuaState = lua_open();
16. luabind::open(myLuaState);
17.
18. // 使用LuaBind導出NumberPrinter類
19. luabind::module(myLuaState) [
20. luabind::class_<NumberPrinter>("NumberPrinter")
21. .def(luabind::constructor<int>())
22. .def("print", &NumberPrinter::print)
23. ];
24.
25. // 如今Lua中可使用NumberPinter類了
26. luaL_dostring(
27. myLuaState,
28. "Print2000 = NumberPrinter(2000) "
29. "Print2000:print() "
30. );
31.
32. lua_close(myLuaState);
33.}
爲了註冊一個類,LuaBind提供了class_類。它有一個重載過的成員函數 def() 。這個函數被用來註冊類的成員函數、操做符、構造器、枚舉和屬性。
它將返回this指針,這樣咱們就能夠方便地直接註冊更多的成員。
屬性
LuaBind 也能夠導出類成員變量:
1. template<typename T>
2. struct Point {
3. Point(T X, T Y) :
4. X(X), Y(Y) {}
5.
6. T X, Y;
7. };
8.
9. template<typename T>
10.struct Box {
11. Box(Point<T> UpperLeft, Point<T> LowerRight) :
12. UpperLeft(UpperLeft), LowerRight(LowerRight) {}
13.
14. Point<T> UpperLeft, LowerRight;
15.};
16.
17.int main() {
18. lua_State *myLuaState = lua_open();
19. luabind::open(myLuaState);
20.
21. // 使用LuaBind導出Point<float>類和Box<float>類
22. luabind::module(myLuaState) [
23. luabind::class_<Point<float> >("Point")
24. .def(luabind::constructor<float, float>())
25. .def_readwrite("X", &Point<float>::X)
26. .def_readwrite("Y", &Point<float>::Y),
27.
28. luabind::class_<Box<float> >("Box")
29. .def(luabind::constructor<Point<float>, Point<float> >())
30. .def_readwrite("UpperLeft", &Box<float>::UpperLeft)
31. .def_readwrite("LowerRight", &Box<float>::LowerRight)
32. ];
33.
34. // 如今Lua中可使用爲些類了
35. luaL_dostring(
36. myLuaState,
37. "MyBox = Box(Point(10, 20), Point(30, 40)) "
38. "MyBox.UpperLeft.X = MyBox.LowerRight.Y "
39. );
40.
41. lua_close(myLuaState);
42.}
本例中使用def_readwrite定義類成員,咱們也能夠用def_readonly把類成員定義成只讀。
LuaBind還能夠把C++類導出成支持getter和setter的屬性的Lua類:
1. struct ResourceManager {
2. ResourceManager() :
3. m_ResourceCount(0) {}
4.
5. void loadResource(const string &sFilename) {
6. ++m_ResourceCount;
7. }
8. size_t getResourceCount() const {
9. return m_ResourceCount;
10. }
11.
12. size_t m_ResourceCount;
13.};
14.
15.int main() {
16. lua_State *myLuaState = lua_open();
17. luabind::open(myLuaState);
18.
19. // 導出類,在Lua中調用ResourceCount屬性會調用C++中的ResourceManager::getResourceCount
20. // 屬性定義有點象C++Builder裏的__property定義,呵呵
21. luabind::module(myLuaState) [
22. luabind::class_<ResourceManager>("ResourceManager")
23. .def("loadResource", &ResourceManager::loadResource)
24. .property("ResourceCount", &ResourceManager::getResourceCount)
25. ];
26.
27. try {
28. ResourceManager MyResourceManager;
29.
30. // 把MyResourceManager定義成Lua的全局變量
31. luabind::globals(myLuaState)["MyResourceManager"] = &MyResourceManager;
32.
33. // 調用
34. luaL_dostring(
35. myLuaState,
36. "MyResourceManager:loadResource(\"abc.res\") "
37. "MyResourceManager:loadResource(\"xyz.res\") "
38. " "
39. "ResourceCount = MyResourceManager.ResourceCount "
40. );
41.
42. // 讀出全局變量
43. size_t ResourceCount = luabind::object_cast<size_t>(
44. luabind::globals(myLuaState)["ResourceCount"]
45. );
46. cout << ResourceCount << endl;
47. }
48. catch(const std::exception &TheError) {
49. cerr << TheError.what() << endl;
50. }
51.
52. lua_close(myLuaState);
53.}
附: Lua語法簡介
1.語法約定
Lua語句用分號結尾,不過若是不寫分號,Lua也會本身判斷如何區分每條語句
如:
a=1 b=a*2 --這樣寫沒有問題,但不太好看。
建議一行裏有多個語句時用分號隔開
變量名、函數名之類的命名規則與C語言同樣:由字母,下劃線和數字組成,但第一個字符不能是數字。而且不能和Lua的保留字相同。
Lua是大小寫敏感的
使用兩個減號--做爲單行註釋符,多行註釋使用--[[...--]]
2.類型
Lua是動態類型語言,變量不要類型定義。Lua中有8個基本類型分別爲:nil、boolean、number、string、userdata、function、thread和table。
同一變量能夠隨時改變它的類型,如:
1. a = 10 --number
2. a = "hello" --string
3. a = false --boolean
4. a = {10,"hello",false} --table
5. a = print --function
使用type函數能夠獲得變量當前的類型,如print(type(a));
nil 全部沒有被賦值過的變量默認值爲nil,給變量賦nil能夠收回變量的空間。
boolean 取值false和true。但要注意Lua中全部的值均可以做爲條件。在控制結構的條件中除了false和nil爲假,其餘值都爲真。因此Lua認爲0和空串都是真。(注意,和C不同哦)
number 表示實數,Lua中沒有整數。不用擔憂實數引發的偏差,Lua的numbers能夠處理任何長整數。
string 字符串,Lua中的字符串能夠存聽任何包括0在內的二進制數據。可使用單引號或雙引號表示字符串,和C同樣使用\做爲轉義符。也可使用或 [[...]]表示字符串,它能夠表示多行,並且不解釋轉義符(也能夠是[=[...]=]、[==[]==]、...用於適應各類類型字符串)。另外要注意的是Lua中字符串是不能夠修改的。
function 函數,Lua中的函數也能夠存儲到變量中,能夠做爲其它函數的參數,能夠做爲函數的返回值。
table 表,表是Lua特有的功能強大的東東,它是Lua中惟一的一種數據結構,它能夠用來描述數組,結構,map的功能。
userdata userdata類型用來將任意C語言數據保存在Lua變量中。例如:用標準I/O庫來描述文件。
thread 線程。由coroutine表建立的一種數據類型,能夠實現多線程協同操做。
3.表達式
算術運行符: 加+、減-、乘*、除/、冪^
關係運算符:小於<、大於>、小於等於<=、大於等於>=、等於==、不等~=
邏輯運算符:與and、或or、非not
and和or的運算結果返回值是其中的操做數:
a and b -- 若是a爲false,則返回a,不然返回b
a or b -- 若是a爲true,則返回a,不然返回b
因此C中的三元運算符a?b:c在Lua中能夠這樣寫:(a and b) or c
鏈接運算符:連續兩個小數點..,如:
"hello" .. "world" 結果是 "helloworld"
0 .. 1 結果是 "01",當在一個數字後面寫..時,必須加上空格以防止被解釋錯。
取長度操做符:一元操做 #
字符串的長度是它的字節數,table 的長度被定義成一個整數下標 n,它知足 t[n] 不是 nil 而 t[n+1] 爲nil。
4.基本語法
賦值
a = a + 1
Lua裏的賦值還能夠同時給多個變量賦值。變量列表和值列表的各個元素用逗號分開,賦值語句右邊的值會依次賦給左邊的變量。如:
a, b = 10, 2*x --至關於a=10; b=2*x
x, y = y, x --交換x和y
若是賦值符左右個數不一樣時,Lua會自動丟棄多餘值或以nil補足
局部變量
local i = 10
使用local聲明局部變量,局部變量只在所在的代碼塊內有效。
若是不聲明,默認爲全局變量,這個變量在全部Lua環境中有效。
代碼塊是指一個控制結構內,一個函數體,或者一個chunk(變量被聲明的那個文件或者文本串),也能夠直接使用do...end(至關於C中的{})。
條件
1. if 條件 then
2. then-part
3. elseif 條件n then
4. elseif-part
5. .. --->多個elseif
6. else
7. else-part
8. end;
循環
Lua中的循環有:while循環,repeat-until循環,for循環和for in循環。
循環中能夠用break跳出,Lua語法要求break和return只能是代碼塊的最後一句(放心,正常的代碼都是知足這個要求的,break和 reuturn後面即便有代碼也是執行不到的,再說了,大不了本身加個do...end好了^_^)
如:
1. local i = 1
2. while a[i] do
3. if a[i] == v then break end
4. i = i + 1
5. end
while循環
1. while condition do
2. statements;
3. end;
repeat-until循環:
1. repeat
2. statements;
3. until conditions;
for循環
1. for var=exp1,exp2,exp3 do
2. loop-part
3. end
for將用exp3做爲step從exp1(初始值)到exp2(終止值),執行loop-part。其中exp3能夠省略,默認step=1
for in循環
1. for 變量 in 集合 do
2. loop-part
3. end
實際上,
for var_1, ..., var_n in explist do block end
等價於
1. do
2. local _f, _s, _var = explist
3. while true do
4. local var_1, ... , var_n = _f(_s, _var)
5. _var = var_1
6. if _var == nil then break end
7. block
8. end
9. end
如:
1. a = {"windows","macos","linux",n=3}
2. for k,v in pairs(a) do print(k,v) end
5.函數
1. function 函數名 (參數列表)
2. statements-list;
3. end;
函數也能夠一次返回多個值,如:
1. function foo() return 'a','b','c'; end
2. a,b,c = foo()
在Lua中還能夠直接定義匿名函數,如 print((function() return 'a','b','c' end)())