1. 數(shù)組操作:
在Lua中,“數(shù)組”只是table的一個(gè)別名,是指以一種特殊的方法來(lái)使用table。出于性能原因,Lua的C API為數(shù)組操作提供了專門的函數(shù),如:
void lua_rawgeti(lua_State* L, int index, int key);
void lua_rawseti(lua_State* L, int index, int key);
以上兩個(gè)函數(shù)分別用于讀取和設(shè)置數(shù)組中的元素值。其中index參數(shù)表示待操作的table在棧中的位置,key表示元素在table中的索引值。由于這兩個(gè)函數(shù)均為原始操作,比涉及元表的table訪問(wèn)更快。通常而言,作為數(shù)組使用的table很少會(huì)用到元表。
見(jiàn)如下代碼示例和關(guān)鍵性注釋:
1 #include <stdio.h>
2 #include <string.h>
3 #include <lua.hpp>
4 #include <lauxlib.h>
5 #include <lualib.h>
6
7 extern "C" int mapFunc(lua_State* L)
8 {
9 //檢查L(zhǎng)ua調(diào)用代碼中傳遞的第一個(gè)參數(shù)必須是table。否則將引發(fā)錯(cuò)誤。
10 luaL_checktype(L,1,LUA_TTABLE);
11 luaL_checktype(L,2,LUA_TFUNCTION);
12 //獲取table中的字段數(shù)量,即數(shù)組的元素?cái)?shù)量。
13 int n = lua_objlen(L,1);
14 //Lua中的數(shù)組起始索引習(xí)慣為1,而不是C中的0。
15 for (int i = 1; i <= n; ++i) {
16 lua_pushvalue(L,2); //將Lua參數(shù)中的function(第二個(gè)參數(shù))的副本壓入棧中。
17 lua_rawgeti(L,1,i); //壓入table[i]
18 lua_call(L,1,1); //調(diào)用function(table[i]),并將函數(shù)結(jié)果壓入棧中。
19 lua_rawseti(L,1,i); //table[i] = 函數(shù)返回值,同時(shí)將返回值彈出棧。
20 }
21
22 //無(wú)結(jié)果返回給Lua代碼。
23 return 0;
24 }
2. 字符串操作:
當(dāng)一個(gè)C函數(shù)從Lua收到一個(gè)字符串參數(shù)時(shí),必須遵守兩條規(guī)則:不要在訪問(wèn)字符串時(shí)從棧中將其彈出,不要修改字符串。在Lua的C API中主要提供了兩個(gè)操作Lua字符串的函數(shù),即:
void lua_pushlstring(lua_State *L, const char *s, size_t l);
const char* lua_pushfstring(lua_State* L, const char* fmt, ...);
第一個(gè)API用于截取指定長(zhǎng)度的子字符串,同時(shí)將其壓入棧中。而第二個(gè)API則類似于C庫(kù)中的sprintf函數(shù),并將格式化后的字符串壓入棧中。和sprintf的格式說(shuō)明符不同的是,該函數(shù)只支持%%(表示字符%)、%s(表示字符串)、%d(表示整數(shù))、%f(表示Lua中的number)及%c(表示字符)。除此之外,不支持任何例如寬度和精度的選項(xiàng)。
1 #include <stdio.h>
2 #include <string.h>
3 #include <lua.hpp>
4 #include <lauxlib.h>
5 #include <lualib.h>
6
7 extern "C" int splitFunc(lua_State* L)
8 {
9 const char* s = luaL_checkstring(L,1);
10 const char* sep = luaL_checkstring(L,2); //分隔符
11 const char* e;
12 int i = 1;
13 lua_newtable(L); //結(jié)果table
14 while ((e = strchr(s,*sep)) != NULL) {
15 lua_pushlstring(L,s,e - s); //壓入子字符串。
16 //將剛剛壓入的子字符串設(shè)置給table,同時(shí)賦值指定的索引值。
17 lua_rawseti(L,-2,i++);
18 s = e + 1;
19 }
20 //壓入最后一個(gè)子串
21 lua_pushstring(L,s);
22 lua_rawseti(L,-2,i);
23 return 1; //返回table。
24 }
Lua API中提供了lua_concat函數(shù),其功能類似于Lua中的".."操作符,用于連接(并彈出)棧頂?shù)膎個(gè)值,然后壓入連接后的結(jié)果。其原型為:
void lua_concat(lua_State *L, int n);
參數(shù)n表示棧中待連接的字符串?dāng)?shù)量。該函數(shù)會(huì)調(diào)用元方法。然而需要說(shuō)明的是,如果連接的字符串?dāng)?shù)量較少,該函數(shù)可以很好的工作,反之,則會(huì)帶來(lái)性能問(wèn)題。為此,Lua API提供了另外一組函數(shù)專門解決由此而帶來(lái)的性能問(wèn)題,見(jiàn)如下代碼示例:
1 #include <stdio.h>
2 #include <string.h>
3 #include <lua.hpp>
4 #include <lauxlib.h>
5 #include <lualib.h>
6
7 extern "C" int strUpperFunc(lua_State* L)
8 {
9 size_t len;
10 luaL_Buffer b;
11 //檢查參數(shù)第一個(gè)參數(shù)是否為字符串,同時(shí)返回字符串的指針及長(zhǎng)度。
12 const char* s = luaL_checklstring(L,1,&len);
13 //初始化Lua的內(nèi)部Buffer。
14 luaL_buffinit(L,&b);
15 //將處理后的字符依次(luaL_addchar)追加到Lua的內(nèi)部Buffer中。
16 for (int i = 0; i < len; ++i)
17 luaL_addchar(&b,toupper(s[i]));
18 //將該Buffer及其內(nèi)容壓入棧中。
19 luaL_pushresult(&b);
20 return 1;
21 }
使用緩沖機(jī)制的第一步是聲明一個(gè)luaL_Buffer變量,并用luaL_buffinit來(lái)初始化它。初始化后,就可通過(guò)luaL_addchar將一個(gè)字符放入緩沖。除該函數(shù)之外,Lua的輔助庫(kù)還提供了直接添加字符串的函數(shù),如:
void luaL_addlstring(luaL_Buffer* b, const char* s, size_t len);
void luaL_addstring(luaL_Buffer* b, const char* s);
最后luaL_pushresult會(huì)更新緩沖,并將最終的字符串留在棧頂。通過(guò)這些函數(shù),就無(wú)須再關(guān)心緩沖的分配了。但是在追加的過(guò)程中,緩沖會(huì)將一些中間結(jié)果放到棧中。因此,在使用時(shí)要留意此細(xì)節(jié),只要保證壓入和彈出的次數(shù)相等既可。Lua API還提供一個(gè)比較常用的函數(shù),用于將棧頂?shù)淖址驍?shù)字也追加到緩沖區(qū)中,函數(shù)原型為:
void luaL_addvalue(luaL_Buffer* b);
3. 在C函數(shù)中保存狀態(tài):
Lua API提供了三種方式來(lái)保存非局部變量,即注冊(cè)表、環(huán)境和upvalue。
1). 注冊(cè)表:
注冊(cè)表是一個(gè)全局的table,只能被C代碼訪問(wèn)。通常用于保存多個(gè)模塊間的共享數(shù)據(jù)。我們可以通過(guò)LUA_REGISTRYINDEX索引值來(lái)訪問(wèn)注冊(cè)表。
1 #include <stdio.h>
2 #include <string.h>
3 #include <lua.hpp>
4 #include <lauxlib.h>
5 #include <lualib.h>
6
7 void registryTestFunc(lua_State* L)
8 {
9 lua_pushstring(L,"Hello");
10 lua_setfield(L,LUA_REGISTRYINDEX,"key1");
11 lua_getfield(L,LUA_REGISTRYINDEX,"key1");
12 printf("%s\n",lua_tostring(L,-1));
13 }
14
15 int main()
16 {
17 lua_State* L = luaL_newstate();
18 registryTestFunc(L);
19 lua_close(L);
20 return 0;
21 }
2). 環(huán)境:
如果需要保存一個(gè)模塊的私有數(shù)據(jù),即模塊內(nèi)各函數(shù)需要共享的數(shù)據(jù),應(yīng)該使用環(huán)境。我們可以通過(guò)LUA_ENVIRONINDEX索引值來(lái)訪問(wèn)環(huán)境。
1 #include <lua.hpp>
2 #include <lauxlib.h>
3 #include <lualib.h>
4
5 //模塊內(nèi)設(shè)置環(huán)境數(shù)據(jù)的函數(shù)
6 extern "C" int setValue(lua_State* L)
7 {
8 lua_pushstring(L,"Hello");
9 lua_setfield(L,LUA_ENVIRONINDEX,"key1");
10 return 0;
11 }
12
13 //模塊內(nèi)獲取環(huán)境數(shù)據(jù)的函數(shù)
14 extern "C" int getValue(lua_State* L)
15 {
16 lua_getfield(L,LUA_ENVIRONINDEX,"key1");
17 printf("%s\n",lua_tostring(L,-1));
18 return 0;
19 }
20
21 static luaL_Reg myfuncs[] = {
22 {"setValue", setValue},
23 {"getValue", getValue},
24 {NULL, NULL}
25 };
26
27
28 extern "C" __declspec(dllexport)
29 int luaopen_testenv(lua_State* L)
30 {
31 lua_newtable(L); //創(chuàng)建一個(gè)新的表用于環(huán)境
32 lua_replace(L,LUA_ENVIRONINDEX); //將剛剛創(chuàng)建并壓入棧的新表替換為當(dāng)前模塊的環(huán)境表。
33 luaL_register(L,"testenv",myfuncs);
34 return 1;
35 }
Lua測(cè)試代碼如下。
1 require "testenv"
2
3 print(testenv.setValue())
4 print(testenv.getValue())
5 --輸出為:Hello
3). upvalue:
upvalue是和特定函數(shù)關(guān)聯(lián)的,我們可以將其簡(jiǎn)單的理解為函數(shù)內(nèi)的靜態(tài)變量。

1 #include <lua.hpp>
2 #include <lauxlib.h>
3 #include <lualib.h>
4
5 extern "C" int counter(lua_State* L)
6 {
7 //獲取第一個(gè)upvalue的值。
8 int val = lua_tointeger(L,lua_upvalueindex(1));
9 //將得到的結(jié)果壓入棧中。
10 lua_pushinteger(L,++val);
11 //賦值一份棧頂?shù)臄?shù)據(jù),以便于后面的替換操作。
12 lua_pushvalue(L,-1);
13 //該函數(shù)將棧頂?shù)臄?shù)據(jù)替換到upvalue(1)中的值。同時(shí)將棧頂數(shù)據(jù)彈出。
14 lua_replace(L,lua_upvalueindex(1));
15 //lua_pushinteger(L,++value)中壓入的數(shù)據(jù)仍然保留在棧中并返回給Lua。
16 return 1;
17 }
18
19 extern "C" int newCounter(lua_State* L)
20 {
21 //壓入一個(gè)upvalue的初始值0,該函數(shù)必須先于lua_pushcclosure之前調(diào)用。
22 lua_pushinteger(L,0);
23 //壓入閉包函數(shù),參數(shù)1表示該閉包函數(shù)的upvalue數(shù)量。該函數(shù)返回值,閉包函數(shù)始終位于棧頂。
24 lua_pushcclosure(L,counter,1);
25 return 1;
26 }
27
28 static luaL_Reg myfuncs[] = {
29 {"counter", counter},
30 {"newCounter", newCounter},
31 {NULL, NULL}
32 };
33
34
35 extern "C" __declspec(dllexport)
36 int luaopen_testupvalue(lua_State* L)
37 {
38 luaL_register(L,"testupvalue",myfuncs);
39 return 1;
40 }

Lua測(cè)試代碼如下。
1 require "testupvalue"
2
3 func = testupvalue.newCounter();
4 print(func());
5 print(func());
6 print(func());
7
8 func = testupvalue.newCounter();
9 print(func());
10 print(func());
11 print(func());
12
13 --[[ 輸出結(jié)果為: 14 1 15 2 16 3 17 1 18 2 19 3 20 --]]