1. 數組操作:
在Lua中,“數組”只是table的一個別名,是指以一種特殊的方法來使用table。出于性能原因,Lua的C API為數組操作提供了專門的函數,如:
見如下代碼示例和關鍵性注釋:
extern "C" int mapFunc(lua_State* L)
{
//檢查Lua調用代碼中傳遞的第一個參數必須是table。否則將引發錯誤。
luaL_checktype(L,1,LUA_TTABLE);
luaL_checktype(L,2,LUA_TFUNCTION);
//獲取table中的字段數量,即數組的元素數量。
int n = lua_objlen(L,1);
//Lua中的數組起始索引習慣為1,而不是C中的0。
for (int i = 1; i <= n; ++i) {
lua_pushvalue(L,2); //將Lua參數中的function(第二個參數)的副本壓入棧中。
lua_rawgeti(L,1,i); //壓入table[i]
lua_call(L,1,1); //調用function(table[i]),并將函數結果壓入棧中。
lua_rawseti(L,1,i); //table[i] = 函數返回值,同時將返回值彈出棧。
}
//無結果返回給Lua代碼。
return 0;
}
2. 字符串操作:
當一個C函數從Lua收到一個字符串參數時,必須遵守兩條規則:不要在訪問字符串時從棧中將其彈出,不要修改字符串。在Lua的C API中主要提供了兩個操作Lua字符串的函數,即:
extern "C" int splitFunc(lua_State* L)
{
const char* s = luaL_checkstring(L,1);
const char* sep = luaL_checkstring(L,2); //分隔符
const char* e;
int i = 1;
lua_newtable(L); //結果table
while ((e = strchr(s,*sep)) != NULL) {
lua_pushlstring(L,s,e - s); //壓入子字符串。
//將剛剛壓入的子字符串設置給table,同時賦值指定的索引值。
lua_rawseti(L,-2,i++);
s = e + 1;
}
//壓入最后一個子串
lua_pushstring(L,s);
lua_rawseti(L,-2,i);
return 1; //返回table。
}
Lua API中提供了lua_concat函數,其功能類似于Lua中的".."操作符,用于連接(并彈出)棧頂的n個值,然后壓入連接后的結果。其原型為:
void lua_concat(lua_State *L, int n);
參數n表示棧中待連接的字符串數量。該函數會調用元方法。然而需要說明的是,如果連接的字符串數量較少,該函數可以很好的工作,反之,則會帶來性能問題。為此,Lua API提供了另外一組函數專門解決由此而帶來的性能問題,見如下代碼示例:
extern "C" int strUpperFunc(lua_State* L)
{
size_t len;
luaL_Buffer b;
//檢查參數第一個參數是否為字符串,同時返回字符串的指針及長度。
const char* s = luaL_checklstring(L,1,&len);
//初始化Lua的內部Buffer。
luaL_buffinit(L,&b);
//將處理后的字符依次(luaL_addchar)追加到Lua的內部Buffer中。
for (int i = 0; i < len; ++i)
luaL_addchar(&b,toupper(s[i]));
//將該Buffer及其內容壓入棧中。
luaL_pushresult(&b);
return 1;
}
使用緩沖機制的第一步是聲明一個luaL_Buffer變量,并用luaL_buffinit來初始化它。初始化后,就可通過luaL_addchar將一個字符放入緩沖。除該函數之外,Lua的輔助庫還提供了直接添加字符串的函數,如:
void registryTestFunc(lua_State* L)
{
lua_pushstring(L,"Hello");
lua_setfield(L,LUA_REGISTRYINDEX,"key1");
lua_getfield(L,LUA_REGISTRYINDEX,"key1");
printf("%s/n",lua_tostring(L,-1));
}
int main()
{
lua_State* L = luaL_newstate();
registryTestFunc(L);
lua_close(L);
return 0;
}
2). 環境:
如果需要保存一個模塊的私有數據,即模塊內各函數需要共享的數據,應該使用環境。我們可以通過LUA_ENVIRONINDEX索引值來訪問環境。
//模塊內設置環境數據的函數
extern "C" int setValue(lua_State* L)
{
lua_pushstring(L,"Hello");
lua_setfield(L,LUA_ENVIRONINDEX,"key1");
return 0;
}
//模塊內獲取環境數據的函數
extern "C" int getValue(lua_State* L)
{
lua_getfield(L,LUA_ENVIRONINDEX,"key1");
printf("%s/n",lua_tostring(L,-1));
return 0;
}
static luaL_Reg myfuncs[] = {
{"setValue", setValue},
{"getValue", getValue},
{NULL, NULL}
};
extern "C" __declspec(dllexport)
int luaopen_testenv(lua_State* L)
{
lua_newtable(L); //創建一個新的表用于環境
lua_replace(L,LUA_ENVIRONINDEX); //將剛剛創建并壓入棧的新表替換為當前模塊的環境表。
luaL_register(L,"testenv",myfuncs);
return 1;
}
Lua測試代碼如下。
extern "C" int counter(lua_State* L)
{
//獲取第一個upvalue的值。
int val = lua_tointeger(L,lua_upvalueindex(1));
//將得到的結果壓入棧中。
lua_pushinteger(L,++val);
//賦值一份棧頂的數據,以便于后面的替換操作。
lua_pushvalue(L,-1);
//該函數將棧頂的數據替換到upvalue(1)中的值。同時將棧頂數據彈出。
lua_replace(L,lua_upvalueindex(1));
//lua_pushinteger(L,++value)中壓入的數據仍然保留在棧中并返回給Lua。
return 1;
}
extern "C" int newCounter(lua_State* L)
{
//壓入一個upvalue的初始值0,該函數必須先于lua_pushcclosure之前調用。
lua_pushinteger(L,0);
//壓入閉包函數,參數1表示該閉包函數的upvalue數量。該函數返回值,閉包函數始終位于棧頂。
lua_pushcclosure(L,counter,1);
return 1;
}
static luaL_Reg myfuncs[] = {
{"counter", counter},
{"newCounter", newCounter},
{NULL, NULL}
};
extern "C" __declspec(dllexport)
int luaopen_testupvalue(lua_State* L)
{
luaL_register(L,"testupvalue",myfuncs);
return 1;
}
Lua測試代碼如下。
func = testupvalue.newCounter();
print(func());
print(func());
print(func());
func = testupvalue.newCounter();
print(func());
print(func());
print(func());
--[[ 輸出結果為:
1
2
3
1
2
3
--]]
新聞熱點
疑難解答