7. 常用的 C API

2018-02-24 16:10 更新

基礎(chǔ)概念

states

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_newstate

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_close

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)存。

lua_load/lua_call/lua_pcall/lua_cpcall

這些函數(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ù)的大概作用:

  • luaL_newstate():創(chuàng)建一個(gè)新的Lua虛擬機(jī)
  • luaL_openlibs():打開(kāi)一些必要的庫(kù),比如print等
  • luaL_loadfile():手冊(cè)上寫(xiě)的是"This function uses lua_load to load the chunk in the filenamed filename." 而lua_load就是把編譯過(guò)的chunk放在stack的頂部。理解chunk很重要,后面會(huì)具體講到
  • lua_pcall:執(zhí)行棧上的函數(shù)調(diào)用

一開(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)型不同而已。

lua_getfield/lua_setfield

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/lua_setglobal

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)

lua_gettop/lua_settop/lua_pop

在任何時(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è)元素刪除。

lua_pushvalue/lua_insert/lua_remove/lua_replace

 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*

lua_gettable/lua_settable

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 事件。

lua_concat

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é)果放到棧頂。

lua_type/lua_typename

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);

lua_checkstack

當(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)的尺寸,并不變化。

lua_is***

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。

lua_to***

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)試信息。

lua_push***

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很象但有如下的重要不同:

  • 你不用申請(qǐng)內(nèi)存去保存格式化結(jié)果,這結(jié)果是一個(gè)lua字符串并且lua自己會(huì)小心管理內(nèi)存(并通過(guò)垃圾收集機(jī)制釋放)。
  • 使用轉(zhuǎn)義字符受限。它們沒(méi)有標(biāo)志量、寬度和精確度。轉(zhuǎn)義字符能夠是’%%’(插入一個(gè)”%”)、’%s’(插入一個(gè)以0結(jié)尾的字符串)、’%f’(插入一個(gè)lua_Number)、’%d’(插入一個(gè)int)和’%c’(插入一個(gè)用int表示的字符)。

lua_register

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;
}
以上內(nèi)容是否對(duì)您有幫助:
在線(xiàn)筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)