Lua連接庫(kù)是完全可重入的,因?yàn)樗鼪](méi)有全局變量。Lua解釋器的整個(gè)state(如全局變量、堆棧等)都存儲(chǔ)在一個(gè)結(jié)構(gòu)類(lèi)型為L(zhǎng)ua_State動(dòng)態(tài)分配的對(duì)象里。指向這一對(duì)象的指針必須作為第一個(gè)參數(shù)傳遞給所有連接庫(kù)的API,除了用來(lái)生成一個(gè)Lua state的函數(shù)——lua_open。在調(diào)用所有的API函數(shù)之前,你必須先用lua_open以生成一個(gè)state:
lua_State* lua_open(void);
可以通過(guò)調(diào)用lua_close來(lái)釋放一個(gè)通過(guò)lua_open生成的state:
void lua_close (lua_State *L);
這一函數(shù)銷(xiāo)毀給定的Lua_State中的所有對(duì)象并釋放state所占用的動(dòng)態(tài)內(nèi)存(如果有必要的話(huà)將通過(guò)調(diào)用對(duì)應(yīng)的垃圾收集元方法來(lái)完成),在某些平臺(tái)上,你不必調(diào)用這個(gè)函數(shù),因?yàn)楫?dāng)宿主程序退出時(shí)會(huì)釋放所有的資源,換句話(huà)說(shuō),長(zhǎng)期運(yùn)行的程序,如守護(hù)進(jìn)程或web服務(wù)器,應(yīng)盡快釋放state所占的資源,以避免其過(guò)于龐大。
Lua使用虛擬堆棧機(jī)制和C程序互相傳值,所有的堆棧中的元素都可以看作一個(gè)Lua值(如nil, number, string等)。
當(dāng)Lua調(diào)用C函數(shù)時(shí),被調(diào)用的C函數(shù)將得到一個(gè)新的堆棧。這一堆棧與之前調(diào)用此函數(shù)的堆棧無(wú)關(guān),也有其它C函數(shù)的堆棧無(wú)關(guān)。這一新的堆棧用調(diào)用C函數(shù)要用到的參數(shù)初始化,同時(shí),這一堆棧也被用以返回函數(shù)調(diào)用結(jié)果。
為了便于操作,在API的中大量操作都并不依從堆棧只能操作棧頂元素的嚴(yán)格規(guī)則。而通過(guò)索引引用堆棧的任一元素。一個(gè)正整數(shù)索引可以看作某一元素在堆棧中的絕對(duì)位置(從1開(kāi)始計(jì)數(shù)),一個(gè)負(fù)整數(shù)索引可以看作某一元素相對(duì)于棧頂?shù)钠屏俊?/p>
特別地,如果堆棧中有n個(gè)元素,那么索引1指向第一個(gè)元素(即第一個(gè)壓入棧的元素)索引n指向最后一個(gè)元素;反過(guò)來(lái),索引-1指向最后一個(gè)元素(即棧頂元素)索引-n指向第一個(gè)元素。當(dāng)一個(gè)索引大于1并小于n時(shí)我們稱(chēng)其為一個(gè)有效索引(即1 <= abs(index) <= top)。
lua_State *lua_newstate (lua_Alloc f, void *ud);
創(chuàng)建一個(gè)新的獨(dú)立 state,不能創(chuàng)建返回 NULL。形參 f 是 allocator 函數(shù),Lua 通過(guò)這個(gè)函數(shù)來(lái)為這個(gè) state 分配內(nèi)存。第二個(gè)形參 ud,是一個(gè)透明指針,每次調(diào)用時(shí),Lua簡(jiǎn)單地傳給 allocator 函數(shù)。
lua_open 被 lua_newstate 替換,可以使用luaL_newstate從標(biāo)準(zhǔn)庫(kù)中創(chuàng)建一個(gè)標(biāo)準(zhǔn)配置的 state,如: lua_State *L = luaL_newstate(); 。
void lua_close (lua_State *L);
銷(xiāo)毀指定的 state 中所有的對(duì)象,并釋放指定的 state 中使用的所有動(dòng)態(tài)內(nèi)存。
這些函數(shù)的目的就是讓我們能夠執(zhí)行壓入棧中的函數(shù),該函數(shù)可能是lua中定義的函數(shù),可能是C++重定義的函數(shù),當(dāng)然我們一般是用來(lái)執(zhí)行l(wèi)ua中執(zhí)行的函數(shù),C++中定義的基本上可以直接調(diào)用的。
int lua_load (lua_State *L,
lua_Reader reader,
void *data,
const char *chunkname);
void lua_call(lua_State *L, int nargs, int nresults);
void lua_pcall(lua_State *L, int nargs, int nresults, int errfunc);
void lua_cpcall(lua_State *L, int nargs, int nresults, int errfunc, void *ud);
L是執(zhí)行環(huán)境,可以理解為當(dāng)前棧,nargs參數(shù)個(gè)數(shù),nresults返回值個(gè)數(shù)。lua_pcall和該函數(shù)區(qū)別是多一個(gè)參數(shù),用于發(fā)生錯(cuò)誤處理時(shí)的代碼返回。lua_cpcall則又多一個(gè)用于傳遞用戶(hù)自定義的數(shù)據(jù)結(jié)構(gòu)的指針。
lua_call的運(yùn)行是無(wú)保護(hù)的,他與lua_pcall相似,但是在錯(cuò)誤發(fā)生的時(shí)候她拋出錯(cuò)誤而不是返回錯(cuò)誤代碼。當(dāng)你在應(yīng)用程序中寫(xiě)主流程的代碼時(shí),不應(yīng)該使用 lua_call,因?yàn)槟銘?yīng)該捕捉任何可能發(fā)生的錯(cuò)誤。當(dāng)你寫(xiě)一個(gè)函數(shù)的代碼時(shí),使用lua_call是比較好的想法,如果有錯(cuò)誤發(fā)生,把錯(cuò)誤留給關(guān)心她的人去處理。所以,寫(xiě)應(yīng)用程序主流程代碼用lua_pcall,寫(xiě)C Native Function代碼時(shí)用lua_call。
示例1:
Lua 代碼:
a = f("how", t.x, 14)
C 代碼:
lua_getfield(L, LUA_GLOBALSINDEX, "f"); /* function to be called */
lua_pushstring(L, "how"); /* 1st argument */
lua_getfield(L, LUA_GLOBALSINDEX, "t"); /* table to be indexed */
lua_getfield(L, -1, "x"); /* push result of t.x (2nd arg) */
lua_remove(L, -2); /* remove 't' from the stack */
lua_pushinteger(L, 14); /* 3rd argument */
lua_call(L, 3, 1); /* call 'f' with 3 arguments and 1 result */
lua_setfield(L, LUA_GLOBALSINDEX, "a"); /* set global 'a' */
在上面的例子除了描述了lua_call的使用外,還對(duì)lua_getfield的使用有一定的參考價(jià)值。特別是學(xué)習(xí)如何在一個(gè)表中獲取他的值。
在上面的例子中,可能再調(diào)用lua_getfield時(shí)就會(huì)忘記調(diào)用lua_remove,當(dāng)然這是我想象自己使用時(shí)會(huì)犯下的錯(cuò)。lua_getfield函數(shù)功能是從指定表中取出指定元素的值并壓棧。上面獲取t.x的值的過(guò)程就是先調(diào)用:
lua_getfield(L, LUA_GLOBALSINDEX, "t");
從全局表中獲取t的值,然而t本身是一個(gè)表,現(xiàn)在棧頂?shù)闹凳莟表。于是再一次調(diào)用:
lua_getfield(L, -1, "x");
從t中取出x的值放到棧上,-1表示棧頂。那該函數(shù)執(zhí)行完成后t的位置由-1就變成-2了,所以下面一句 lua_remove 索引的是-2,必須把t給remove掉,否則棧中就是4個(gè)參數(shù)了。上面的最后一句 lua_setfield 的目的是把返回值取回賦給全局變量a,因?yàn)樵趌ua_call執(zhí)行完成后,棧頂?shù)木褪欠祷刂盗?/strong>。
示例2:
//test.lua
function printmsg()
print("hello world")
end
x = 10
//test.c
#include <stdio.h>
#include <unistd.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
int main(int argc, const char *argv[]) {
lua_State *L;
if(NULL == (L = luaL_newstate())) {
perror("luaL_newstate failed");
return -1;
}
luaL_openlibs(L);
if(luaL_loadfile(L, "./test.lua")) {
perror("loadfile failed");
return -1;
}
lua_pcall(L, 0, 0, 0);
lua_getglobal(L, "printmsg");
lua_pcall(L, 0, 0, 0);
lua_close(L);
return 0;
}
上面的代碼就是在test.c中調(diào)用test.lua的函數(shù)printmsg函數(shù)。
對(duì)于上面的C代碼,我想大家都知道幾個(gè)函數(shù)的大概作用:
一開(kāi)始我一直認(rèn)為既然 luaL_loadfile 執(zhí)行以后,就可以直接用 lua_getglobal 獲得test.lua中的函數(shù),其實(shí)不然。手冊(cè)中明確提到,lua_load把一個(gè)lua文件當(dāng)作一個(gè)chunk編譯后放到stack的棧頂,而什么是chunk呢?chunk就是一個(gè)可執(zhí)行語(yǔ)句的組合,可以是一個(gè)文件也可以是一個(gè)string,“Lua handles a chunk as the body of an anonymous function with a variable number of arguments”這是Lua對(duì)chunk也就是lua文件的處理方式,就是認(rèn)為是一個(gè)可變參數(shù)的匿名函數(shù)。也就是說(shuō),調(diào)用后棧上有一個(gè)匿名函數(shù),這個(gè)函數(shù)的body就是文件中所有的內(nèi)容。
在 luaL_loadfile 后,調(diào)用 lua_gettop 以及 lua_type 可以知道棧的大小為1,放在棧上的是一個(gè) function 類(lèi)型的value。為什么 loadfile 后我們不能直接獲取到 printmsg 這個(gè)函數(shù)呢,那是因?yàn)閯偛盘岬降?,loadfile僅僅視編譯lua文件,并不執(zhí)行這個(gè)文件,也就是說(shuō)只是在棧上形成了一個(gè)匿名函數(shù)。只有執(zhí)行這個(gè)函數(shù)一次,才會(huì)使得printmsg可以通過(guò) lua_getglobal 獲取,否則,全局變量是空的。我在手冊(cè)上看到這樣一句話(huà):Lua在執(zhí)行函數(shù)的時(shí)候,函數(shù)會(huì)實(shí)例化,獲得的 closure 也是這個(gè)函數(shù)的最終值。其實(shí)不管是函數(shù),還是其他類(lèi)型,如果不執(zhí)行的話(huà),它們只是被編譯,并不能在進(jìn)程的空間種獲取到他們,感覺(jué)就像c的庫(kù)一樣,他們的編譯文件.so已經(jīng)存在,但是如果你不調(diào)用它,那么庫(kù)中所有的變量不能被實(shí)例化,調(diào)用者也就無(wú)法訪(fǎng)問(wèn)。其實(shí)pringmsg和x本質(zhì)是一樣的,只是他們類(lèi)型不同而已。
void lua_getfield (lua_State *L, int index, const char *k);
把值 t[k] 壓入堆棧,t 是給定有效的索引 index 的值,和在 Lua 中一樣,這個(gè)函數(shù)可能會(huì)觸發(fā)元方法 index 事件。
void lua_setfield (lua_State *L, int index, const char *k);
相當(dāng)于 t[k] = v,t 是給定的有效索引 index 的值,v 是堆棧頂部的值,這個(gè)函數(shù)會(huì)彈出這個(gè)值,和在 Lua 中一樣,這個(gè)函數(shù)可能會(huì)觸發(fā) newindex 元方法事件。
lua_getglobal
void lua_getglobal (lua_State *L, const char *name);
把全局 name 的值壓入棧頂,它被定義為宏(macro):
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, s)
lua_setglobal
void lua_setglobal (lua_State *L, const char *name);
從棧中彈出一個(gè)值并賦值給全局 name,它被定義成宏(macro):
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, s)
在任何時(shí)候,你都可以通過(guò)調(diào)用lua_gettop函數(shù)取得棧頂元素的索引:
int lua_gettop (lua_State *L);
因?yàn)樗饕龔?開(kāi)始計(jì)數(shù),lua_gettop的返回值等于這個(gè)堆棧的元素個(gè)數(shù)(當(dāng)堆棧為空時(shí)返回值為0)
void lua_settop (lua_State* L, int index );
lua_settop用于把堆棧的棧頂索引設(shè)置為指定的數(shù)值,它可以接受所有可接受索引。如果新的棧頂索引比原來(lái)的大,則新的位置用nil填充。如果index為0,則將刪除堆棧中的所有元素。在lua.h中定義了如下一個(gè)宏:
#define lua_pop(L,n) lua_settop(L,-(n)-1)
用以把堆棧上部的n個(gè)元素刪除。
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);
lua_pushvalue壓入一個(gè)元素的值拷貝到指定的索引處,相反地,lua_remove刪除給定索引的元素,并將之一索引之上的元素來(lái)填補(bǔ)空缺。同樣地,lua_insert在上移給定索引之上的所有元素后再在指定位置插入新元素。Lua_replace將棧頂元素壓入指定位置而不移動(dòng)任何元素(因此指定位置的元素的值被替換)。這些函數(shù)都僅接受有效索引(你不應(yīng)當(dāng)使用假索引調(diào)用lua_remove或luainsert,因?yàn)樗荒芙馕鰹橐粋€(gè)堆棧位置)。下面是一個(gè)例子,棧的初始狀態(tài)為10 20 30 40 50?(從棧底到棧頂,“_”標(biāo)識(shí)為棧頂,有:
lua_pushvalue(L, 3) --> 10 20 30 40 50 30*
lua_pushvalue(L, -1) --> 10 20 30 40 50 30 30*
lua_remove(L, -3) --> 10 20 30 40 30 30*
lua_remove(L, 6) --> 10 20 30 40 30*
lua_insert(L, 1) --> 30 10 20 30 40*
lua_insert(L, -1) --> 30 10 20 30 40* (沒(méi)影響)
lua_replace(L, 2) --> 30 40 20 30*
lua_settop(L, -3) --> 30 40*
lua_settop(L, 6) --> 30 40 nil nil nil nil*
void lua_gettable (lua_State *L, int index);
把 t[k] 壓入堆棧,t 是給出的有效的索引 index 的值,k 是棧頂?shù)闹?,這個(gè)函數(shù)會(huì)從堆棧中彈出 key,并將結(jié)果值放到它的位置,和在 Lua 一樣,函數(shù)可能會(huì)觸發(fā)一個(gè)元方法 index 事件。
void lua_settable (lua_State *L, int index);
相當(dāng)于 t[k]=v,t 是給出的有效的索引 index 的值,v 是堆棧頂部的值,k 是堆棧頂部下面的值。這個(gè)函數(shù)會(huì)從堆棧中彈出 key 和 value 的值,和在 Lua 中一樣,函數(shù)可能會(huì)觸發(fā)元方法 newindex 事件。
void lua_concat (lua_State *L, int n);
用來(lái)連接字符串,等價(jià)于Lua中的..操作符:自動(dòng)將數(shù)字轉(zhuǎn)換成字符串,如果有必要的時(shí)候還會(huì)自動(dòng)調(diào)用metamethods。另外,她可以同時(shí)連接多個(gè)字符串。調(diào)用lua_concat(L,n)將連接(同時(shí)會(huì)出棧)棧頂?shù)膎個(gè)值,并將最終結(jié)果放到棧頂。
int lua_type (lua_State *L, int index);
lua_type返回堆棧元素的值類(lèi)型,當(dāng)使用無(wú)效索引時(shí)返回LUA_TNONE(如當(dāng)堆棧為空的時(shí)候),lua_type返回的類(lèi)型代碼為如下在lua.h中定義的常量:LUA_TNIL,LUA_TNUMBER,LUA_TBOOLEAN,LUA_TSTRING,LUA_TTABLE,LUA_TFUNCTION,LUA_USERDATA,LUA_TTHEARD,LUA_TLIGHTUSERDATA。下面的函數(shù)可以將這些常量轉(zhuǎn)換為字符串:
const char* lua_typename (lua_State* L, int type);
當(dāng)你使用Lua API的時(shí)候,你有責(zé)任控制堆棧溢出。函數(shù)
int lua_checkstack (lua_State *L, ine extra);
將把堆棧的尺寸擴(kuò)大到可以容納top+extra個(gè)元素;當(dāng)不能擴(kuò)大堆棧尺寸到這一尺寸時(shí)返回假。這一函數(shù)從不減小堆棧的尺寸;當(dāng)前堆棧的尺寸大于新的尺寸時(shí),它將保留原來(lái)的尺寸,并不變化。
int lua_isnumber(lua_State *L, int index);
int lua_isboolean(lua_State *L, int index);
int lua_isfunction(lua_State *L, int index);
int lua_istable(lua_State *L, int index);
int lua_isstring(lua_State *L, int index);
int lua_isnil(lua_State *L, int index);
int lua_iscfunction(lua_State *L, int index);
帶lua_is*前輟的函數(shù)在當(dāng)堆棧元素對(duì)象與給定的類(lèi)型兼容時(shí)返回1,否則返回0。Lua_isboolean是個(gè)例外,它僅在元素類(lèi)型為布爾型時(shí)成功(否則沒(méi)有意思,因?yàn)槿魏沃刀伎煽醋鞑紶栃停?。?dāng)使用無(wú)效索引時(shí),它們總返回0。Lua_isnumber接受數(shù)字或者全部為數(shù)字的字符串;lua_isstring打接受字符串和數(shù)值,lua_isfunction接受lua函數(shù)和C函數(shù);lua_isuserdata也可接受完全和輕量級(jí)兩種userdata。如果想?yún)^(qū)分C函數(shù)和lua函數(shù),可以使用lua_iscfunction函數(shù);同樣地,想?yún)^(qū)分完全和輕量級(jí)userdata可以使用lua_islightuserdata;區(qū)分?jǐn)?shù)字和數(shù)字組成的字符串可以使用lua_type。
API函數(shù)中還有比較堆棧中的兩個(gè)值 的大小的函數(shù):
int lua_equal(lua_State *L, int index1, int index2);
int lua_rawequal(lua_State *L, int index1, int index2);
int lua_lessthan(lua_State *L, int index1, int index2);
lua_equal和lua_lessthan與相對(duì)應(yīng)的lua操作符等價(jià)(參考2.5.2)。lua_rawequal直接判斷兩個(gè)值的原始值,而非通過(guò)調(diào)用元方法來(lái)比較。以上的函數(shù)當(dāng)索引無(wú)效時(shí)返回0。
int lua_toboolean(lua_State *L, int index);
lua_CFunction lua_tocfunction(lua_State *L, int index);
lua_Integer lua_tointeger(lua_State *L, int index);
const char *lua_tolstring(lua_State *L, int index);
lua_Number lua_tonumber(lua_State *L, int index);
void *lua_topointer(lua_State *L, int index);
lua_State *lua_tothread(lua_State *L, int index);
const char *lua_tostring(lua_State *L, int index);
這些函數(shù)可通過(guò)任意可接受索引調(diào)用,如果用無(wú)效索引為參數(shù),則和給定值并不匹配類(lèi)型一樣。 lua_toboolean轉(zhuǎn)換指定索引lua值為C“布爾型”值(0或1)。當(dāng)lua值僅為false或nil時(shí)返回0(如果你僅想接受一個(gè)真正的布爾值,可以先使用lua_isboolean去測(cè)試這個(gè)值的類(lèi)型。
lua_tonumber轉(zhuǎn)換指定索引的值為數(shù)字(lua_Number默認(rèn)為double)。這一lua值必須數(shù)字或可轉(zhuǎn)換為數(shù)字的字符串(參考2.2.1),否則lua_tonumber返回0。
lua_tostring將指定索引的值轉(zhuǎn)換為字符串(const char*)。lua值必須為字符串或數(shù)字,否則返回NULL。當(dāng)值為數(shù)字,lua_tostring將會(huì)把堆棧的原值轉(zhuǎn)換為字符串(當(dāng)lua_tostring應(yīng)用到鍵值上時(shí)會(huì)使lua_next出現(xiàn)難以找出原因的錯(cuò)誤)。lua_tostring返回一個(gè)完全對(duì)齊的字符串指針,這一字符串總是’/0’結(jié)尾(和C一樣),但可能含有其它的0。如果你不知道一個(gè)字符串有多少個(gè)0,你可以使用lua_strlen取得真實(shí)長(zhǎng)度。因?yàn)閘ua有垃圾收集機(jī)制,因此不保證返回的字符串指針在對(duì)應(yīng)的值從堆棧中刪除后仍然有效。如果你以后還要用到當(dāng)前函數(shù)返回的字符串,你應(yīng)當(dāng)備份它或者將它放到registry中(參考3.18)。
lua_tofunction將堆棧中的值轉(zhuǎn)換為C函數(shù)指針,這個(gè)值必須為C函數(shù)指針,否則返回NULL。數(shù)據(jù)類(lèi)型lua_CFunction將在3.16節(jié)講述。
lua_tothread轉(zhuǎn)換堆棧中的值為lua線(xiàn)程(以lua_State*為表現(xiàn)形式),此值必須是一個(gè)線(xiàn)程,否則返回NULL。
lua_topointer轉(zhuǎn)換堆棧中的值為通用C指針(void*)。這個(gè)值必須為userdata、表、線(xiàn)程或函數(shù),否則返回NULL。lua保證同一類(lèi)型的不同對(duì)象返回不同指針。沒(méi)有直接方法將指針轉(zhuǎn)換為原值,這一函數(shù)通常用以獲取調(diào)試信息。
void lua_pushboolean(lua_State *L, int b);
void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n);
void lua_pushcfunction(lua_State *L, lua_CFunction f);
const char *lua_pushfstring (lua_State *L, const char *fmt, ...);
void lua_pushinteger (lua_State *L, lua_Integer n);
void lua_pushliteral
void lua_pushlstring(lua_State *L, const char *s, size_t len);
void lua_pushnil(lua_State *L);
void lua_pushnumber(lua_State *L, lua_Number n);
void lua_pushstring(lua_State *L, const char *s);
const char *lua_pushvfstring (lua_State *L,
const char *fmt,
va_list argp);
這些函數(shù)接受一個(gè)C值,并將其轉(zhuǎn)換為對(duì)應(yīng)的lua值,然后將其壓入堆棧。lua_pushlstring和lua_pushstring對(duì)給定的字符串生成一個(gè)可以互轉(zhuǎn)的拷貝,這是個(gè)例外。lua_pushstring能壓C字符串(即以0結(jié)尾并且內(nèi)部沒(méi)有0),否則建議使用更通用的lua_pushlstring,它能指定長(zhǎng)度。
你同樣可以壓入“格式化”字符串:
const char *lua_pushfstring (lua_State *L, const char *fmt, ...);
const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);
這兩個(gè)函數(shù)向堆棧壓入格式化字符串并返回指向字符串的指針。它們跟sprintf和vsprintf很象但有如下的重要不同:
void lua_register (lua_State *L, const char *name, lua_CFunction f);
設(shè)置 C 函數(shù) f 為新的全局變量 name 的值,它被定義為宏(macro):
#define lua_register(L,n,f) (lua_pushcfunction(L, f), lua_setglobal(L, n))
#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <lauxlib.h>
#include <lualib.h>
void
load(lua_State *L, const char *fname, int *w, int *h) {
if (luaL_loadfile(L, fname) || lua_pcall(L, 0, 0 ,0)) {
printf("Error Msg is %s.\n", lua_tostring(L, -1));
return;
}
lua_getglobal(L, "width"); // #define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
lua_getglobal(L, "height");
if (!lua_isnumber(L, -2)) {
printf("'width' should be a number\n");
return;
}
if (!lua_isnumber(L, -1)) {
printf("'height' should be a number\n", );
return;
}
*w = lua_tointeger(L, -2);
*h = lua_tointeger(L, -1);
}
int
main() {
lua_State *L = luaL_newstate();
int w, h;
load(L, "D:/test.lua", &w, &h);
printf("width = %d, height = %d\n", w, h);
lua_close(L);
return 0;
}
更多建議: