Lua連接庫是完全可重入的,因為它沒有全局變量。Lua解釋器的整個state(如全局變量、堆棧等)都存儲在一個結(jié)構(gòu)類型為Lua_State動態(tài)分配的對象里。指向這一對象的指針必須作為第一個參數(shù)傳遞給所有連接庫的API,除了用來生成一個Lua state的函數(shù)——lua_open。在調(diào)用所有的API函數(shù)之前,你必須先用lua_open以生成一個state:
lua_State* lua_open(void);
可以通過調(diào)用lua_close來釋放一個通過lua_open生成的state:
void lua_close (lua_State *L);
這一函數(shù)銷毀給定的Lua_State中的所有對象并釋放state所占用的動態(tài)內(nèi)存(如果有必要的話將通過調(diào)用對應(yīng)的垃圾收集元方法來完成),在某些平臺上,你不必調(diào)用這個函數(shù),因為當(dāng)宿主程序退出時會釋放所有的資源,換句話說,長期運行的程序,如守護(hù)進(jìn)程或web服務(wù)器,應(yīng)盡快釋放state所占的資源,以避免其過于龐大。
Lua使用虛擬堆棧機制和C程序互相傳值,所有的堆棧中的元素都可以看作一個Lua值(如nil, number, string等)。
當(dāng)Lua調(diào)用C函數(shù)時,被調(diào)用的C函數(shù)將得到一個新的堆棧。這一堆棧與之前調(diào)用此函數(shù)的堆棧無關(guān),也有其它C函數(shù)的堆棧無關(guān)。這一新的堆棧用調(diào)用C函數(shù)要用到的參數(shù)初始化,同時,這一堆棧也被用以返回函數(shù)調(diào)用結(jié)果。
為了便于操作,在API的中大量操作都并不依從堆棧只能操作棧頂元素的嚴(yán)格規(guī)則。而通過索引引用堆棧的任一元素。一個正整數(shù)索引可以看作某一元素在堆棧中的絕對位置(從1開始計數(shù)),一個負(fù)整數(shù)索引可以看作某一元素相對于棧頂?shù)钠屏俊?/p>
特別地,如果堆棧中有n個元素,那么索引1指向第一個元素(即第一個壓入棧的元素)索引n指向最后一個元素;反過來,索引-1指向最后一個元素(即棧頂元素)索引-n指向第一個元素。當(dāng)一個索引大于1并小于n時我們稱其為一個有效索引(即1 <= abs(index) <= top)。
lua_State *lua_newstate (lua_Alloc f, void *ud);
創(chuàng)建一個新的獨立 state,不能創(chuàng)建返回 NULL。形參 f 是 allocator 函數(shù),Lua 通過這個函數(shù)來為這個 state 分配內(nèi)存。第二個形參 ud,是一個透明指針,每次調(diào)用時,Lua簡單地傳給 allocator 函數(shù)。
lua_open 被 lua_newstate 替換,可以使用luaL_newstate從標(biāo)準(zhǔn)庫中創(chuàng)建一個標(biāo)準(zhǔn)配置的 state,如: lua_State *L = luaL_newstate(); 。
void lua_close (lua_State *L);
銷毀指定的 state 中所有的對象,并釋放指定的 state 中使用的所有動態(tài)內(nèi)存。
這些函數(shù)的目的就是讓我們能夠執(zhí)行壓入棧中的函數(shù),該函數(shù)可能是lua中定義的函數(shù),可能是C++重定義的函數(shù),當(dāng)然我們一般是用來執(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ù)個數(shù),nresults返回值個數(shù)。lua_pcall和該函數(shù)區(qū)別是多一個參數(shù),用于發(fā)生錯誤處理時的代碼返回。lua_cpcall則又多一個用于傳遞用戶自定義的數(shù)據(jù)結(jié)構(gòu)的指針。
lua_call的運行是無保護(hù)的,他與lua_pcall相似,但是在錯誤發(fā)生的時候她拋出錯誤而不是返回錯誤代碼。當(dāng)你在應(yīng)用程序中寫主流程的代碼時,不應(yīng)該使用 lua_call,因為你應(yīng)該捕捉任何可能發(fā)生的錯誤。當(dāng)你寫一個函數(shù)的代碼時,使用lua_call是比較好的想法,如果有錯誤發(fā)生,把錯誤留給關(guān)心她的人去處理。所以,寫應(yīng)用程序主流程代碼用lua_pcall,寫C Native Function代碼時用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的使用外,還對lua_getfield的使用有一定的參考價值。特別是學(xué)習(xí)如何在一個表中獲取他的值。
在上面的例子中,可能再調(diào)用lua_getfield時就會忘記調(diào)用lua_remove,當(dāng)然這是我想象自己使用時會犯下的錯。lua_getfield函數(shù)功能是從指定表中取出指定元素的值并壓棧。上面獲取t.x的值的過程就是先調(diào)用:
lua_getfield(L, LUA_GLOBALSINDEX, "t");
從全局表中獲取t的值,然而t本身是一個表,現(xiàn)在棧頂?shù)闹凳莟表。于是再一次調(diào)用:
lua_getfield(L, -1, "x");
從t中取出x的值放到棧上,-1表示棧頂。那該函數(shù)執(zhí)行完成后t的位置由-1就變成-2了,所以下面一句 lua_remove 索引的是-2,必須把t給remove掉,否則棧中就是4個參數(shù)了。上面的最后一句 lua_setfield 的目的是把返回值取回賦給全局變量a,因為在lua_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ù)。
對于上面的C代碼,我想大家都知道幾個函數(shù)的大概作用:
一開始我一直認(rèn)為既然 luaL_loadfile 執(zhí)行以后,就可以直接用 lua_getglobal 獲得test.lua中的函數(shù),其實不然。手冊中明確提到,lua_load把一個lua文件當(dāng)作一個chunk編譯后放到stack的棧頂,而什么是chunk呢?chunk就是一個可執(zhí)行語句的組合,可以是一個文件也可以是一個string,“Lua handles a chunk as the body of an anonymous function with a variable number of arguments”這是Lua對chunk也就是lua文件的處理方式,就是認(rèn)為是一個可變參數(shù)的匿名函數(shù)。也就是說,調(diào)用后棧上有一個匿名函數(shù),這個函數(shù)的body就是文件中所有的內(nèi)容。
在 luaL_loadfile 后,調(diào)用 lua_gettop 以及 lua_type 可以知道棧的大小為1,放在棧上的是一個 function 類型的value。為什么 loadfile 后我們不能直接獲取到 printmsg 這個函數(shù)呢,那是因為剛才提到的,loadfile僅僅視編譯lua文件,并不執(zhí)行這個文件,也就是說只是在棧上形成了一個匿名函數(shù)。只有執(zhí)行這個函數(shù)一次,才會使得printmsg可以通過 lua_getglobal 獲取,否則,全局變量是空的。我在手冊上看到這樣一句話:Lua在執(zhí)行函數(shù)的時候,函數(shù)會實例化,獲得的 closure 也是這個函數(shù)的最終值。其實不管是函數(shù),還是其他類型,如果不執(zhí)行的話,它們只是被編譯,并不能在進(jìn)程的空間種獲取到他們,感覺就像c的庫一樣,他們的編譯文件.so已經(jīng)存在,但是如果你不調(diào)用它,那么庫中所有的變量不能被實例化,調(diào)用者也就無法訪問。其實pringmsg和x本質(zhì)是一樣的,只是他們類型不同而已。
void lua_getfield (lua_State *L, int index, const char *k);
把值 t[k] 壓入堆棧,t 是給定有效的索引 index 的值,和在 Lua 中一樣,這個函數(shù)可能會觸發(fā)元方法 index 事件。
void lua_setfield (lua_State *L, int index, const char *k);
相當(dāng)于 t[k] = v,t 是給定的有效索引 index 的值,v 是堆棧頂部的值,這個函數(shù)會彈出這個值,和在 Lua 中一樣,這個函數(shù)可能會觸發(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);
從棧中彈出一個值并賦值給全局 name,它被定義成宏(macro):
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, s)
在任何時候,你都可以通過調(diào)用lua_gettop函數(shù)取得棧頂元素的索引:
int lua_gettop (lua_State *L);
因為索引從1開始計數(shù),lua_gettop的返回值等于這個堆棧的元素個數(shù)(當(dāng)堆棧為空時返回值為0)
void lua_settop (lua_State* L, int index );
lua_settop用于把堆棧的棧頂索引設(shè)置為指定的數(shù)值,它可以接受所有可接受索引。如果新的棧頂索引比原來的大,則新的位置用nil填充。如果index為0,則將刪除堆棧中的所有元素。在lua.h中定義了如下一個宏:
#define lua_pop(L,n) lua_settop(L,-(n)-1)
用以把堆棧上部的n個元素刪除。
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壓入一個元素的值拷貝到指定的索引處,相反地,lua_remove刪除給定索引的元素,并將之一索引之上的元素來填補空缺。同樣地,lua_insert在上移給定索引之上的所有元素后再在指定位置插入新元素。Lua_replace將棧頂元素壓入指定位置而不移動任何元素(因此指定位置的元素的值被替換)。這些函數(shù)都僅接受有效索引(你不應(yīng)當(dāng)使用假索引調(diào)用lua_remove或luainsert,因為它不能解析為一個堆棧位置)。下面是一個例子,棧的初始狀態(tài)為10 20 30 40 50?(從棧底到棧頂,“_”標(biāo)識為棧頂,有:
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* (沒影響)
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ù)闹?,這個函數(shù)會從堆棧中彈出 key,并將結(jié)果值放到它的位置,和在 Lua 一樣,函數(shù)可能會觸發(fā)一個元方法 index 事件。
void lua_settable (lua_State *L, int index);
相當(dāng)于 t[k]=v,t 是給出的有效的索引 index 的值,v 是堆棧頂部的值,k 是堆棧頂部下面的值。這個函數(shù)會從堆棧中彈出 key 和 value 的值,和在 Lua 中一樣,函數(shù)可能會觸發(fā)元方法 newindex 事件。
void lua_concat (lua_State *L, int n);
用來連接字符串,等價于Lua中的..操作符:自動將數(shù)字轉(zhuǎn)換成字符串,如果有必要的時候還會自動調(diào)用metamethods。另外,她可以同時連接多個字符串。調(diào)用lua_concat(L,n)將連接(同時會出棧)棧頂?shù)膎個值,并將最終結(jié)果放到棧頂。
int lua_type (lua_State *L, int index);
lua_type返回堆棧元素的值類型,當(dāng)使用無效索引時返回LUA_TNONE(如當(dāng)堆棧為空的時候),lua_type返回的類型代碼為如下在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的時候,你有責(zé)任控制堆棧溢出。函數(shù)
int lua_checkstack (lua_State *L, ine extra);
將把堆棧的尺寸擴大到可以容納top+extra個元素;當(dāng)不能擴大堆棧尺寸到這一尺寸時返回假。這一函數(shù)從不減小堆棧的尺寸;當(dāng)前堆棧的尺寸大于新的尺寸時,它將保留原來的尺寸,并不變化。
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)堆棧元素對象與給定的類型兼容時返回1,否則返回0。Lua_isboolean是個例外,它僅在元素類型為布爾型時成功(否則沒有意思,因為任何值都可看作布爾型)。當(dāng)使用無效索引時,它們總返回0。Lua_isnumber接受數(shù)字或者全部為數(shù)字的字符串;lua_isstring打接受字符串和數(shù)值,lua_isfunction接受lua函數(shù)和C函數(shù);lua_isuserdata也可接受完全和輕量級兩種userdata。如果想?yún)^(qū)分C函數(shù)和lua函數(shù),可以使用lua_iscfunction函數(shù);同樣地,想?yún)^(qū)分完全和輕量級userdata可以使用lua_islightuserdata;區(qū)分?jǐn)?shù)字和數(shù)字組成的字符串可以使用lua_type。
API函數(shù)中還有比較堆棧中的兩個值 的大小的函數(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與相對應(yīng)的lua操作符等價(參考2.5.2)。lua_rawequal直接判斷兩個值的原始值,而非通過調(diào)用元方法來比較。以上的函數(shù)當(dāng)索引無效時返回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ù)可通過任意可接受索引調(diào)用,如果用無效索引為參數(shù),則和給定值并不匹配類型一樣。 lua_toboolean轉(zhuǎn)換指定索引lua值為C“布爾型”值(0或1)。當(dāng)lua值僅為false或nil時返回0(如果你僅想接受一個真正的布爾值,可以先使用lua_isboolean去測試這個值的類型。
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將會把堆棧的原值轉(zhuǎn)換為字符串(當(dāng)lua_tostring應(yīng)用到鍵值上時會使lua_next出現(xiàn)難以找出原因的錯誤)。lua_tostring返回一個完全對齊的字符串指針,這一字符串總是’/0’結(jié)尾(和C一樣),但可能含有其它的0。如果你不知道一個字符串有多少個0,你可以使用lua_strlen取得真實長度。因為lua有垃圾收集機制,因此不保證返回的字符串指針在對應(yīng)的值從堆棧中刪除后仍然有效。如果你以后還要用到當(dāng)前函數(shù)返回的字符串,你應(yīng)當(dāng)備份它或者將它放到registry中(參考3.18)。
lua_tofunction將堆棧中的值轉(zhuǎn)換為C函數(shù)指針,這個值必須為C函數(shù)指針,否則返回NULL。數(shù)據(jù)類型lua_CFunction將在3.16節(jié)講述。
lua_tothread轉(zhuǎn)換堆棧中的值為lua線程(以lua_State*為表現(xiàn)形式),此值必須是一個線程,否則返回NULL。
lua_topointer轉(zhuǎn)換堆棧中的值為通用C指針(void*)。這個值必須為userdata、表、線程或函數(shù),否則返回NULL。lua保證同一類型的不同對象返回不同指針。沒有直接方法將指針轉(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ù)接受一個C值,并將其轉(zhuǎn)換為對應(yīng)的lua值,然后將其壓入堆棧。lua_pushlstring和lua_pushstring對給定的字符串生成一個可以互轉(zhuǎn)的拷貝,這是個例外。lua_pushstring能壓C字符串(即以0結(jié)尾并且內(nèi)部沒有0),否則建議使用更通用的lua_pushlstring,它能指定長度。
你同樣可以壓入“格式化”字符串:
const char *lua_pushfstring (lua_State *L, const char *fmt, ...);
const char *lua_pushvfstring (lua_State *L, const char *fmt, va_list argp);
這兩個函數(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;
}
更多建議: