本文將介紹如何在C/C++里面操作Lua的數組和字符串類型,同時還會介紹如何在C/C++函數里面存儲Lua狀態(registry和upvalue),而registry在使用C/C++自定義類型時非常有用,可以方便地為userdata指定metatable。
C/C++操作Lua數組
Lua數組Overview
在Lua里面,數組只不過是key為整數的table而已。比如一個table為array = {12,”Hello”, “World”},它是一個數組,可以用下面的代碼來訪問它:
復制代碼 代碼如下:
print(array[1]) --這里會輸出array的第一個元素12。
print(array[3]) --這里會輸出array的第三個元素World
需要注意的一點就是:Lua的數組的下標是從1開始的。如果你使用下面的語句則會輸出nil值:
復制代碼 代碼如下:
print(array[0]) --輸出nil
print(array["1"]) --輸出nil(想想和array[1]的區別:一個是integer作為key,一個是字符串做為key)
通用Table操作方法
之前我們在教程1中介紹了如何傳遞Table給Lua,以及在教程3中介紹了如何訪問Table的數據。因為數組也是Table,所以我們可以用同樣的方式來讀取數組。
讀取數組
假設我們的Lua Table為array = {“Hello”, 1, “World”, 23.2},那么我們可以用下列函數來訪問它:
復制代碼 代碼如下:
void readLuaArray(lua_State *L)
{
lua_settop(L,0); //這樣確保我們的array是放在當前棧的棧頂。
lua_getglobal(L, "array");
//如果前面不調用lua_settop(L,0),那我們必須要使用luaL_len(L,-1)
int n = luaL_len(L, 1); //luaL_len可以獲得table的元素個數
for (int i = 1; i = n; ++i) {
lua_pushnumber(L, i); //往棧里面壓入i
lua_gettable(L, -2); //讀取table[i],table位于-2的位置。
//lua_rawget(L, -2); //lua_gettable也可以用lua_rawget來替換
coutlua_tostring(L, -1)endl;
lua_pop(L, 1);
}
}
最后輸出的結果為:
復制代碼 代碼如下:
"Hello", 1, "World", 23.2
修改數組
現在我們如果想要修改這個數組,把每一個數組的元素都變成”hehe[i]”(i = 1-n),我們看看怎么做。
復制代碼 代碼如下:
int writeLuaArray(lua_State *L)
{
lua_settop(L, 0);
lua_getglobal(L, "array");
//確保第一個函數一個要是一個table
luaL_checktype(L, 1, LUA_TTABLE);
int n = luaL_len(L,1);
for (int i = 1; i = n; ++i) {
lua_pushnumber(L, i);
char buf[256];
sprintf(buf, "hehe%d", i);
lua_pushstring(L, buf);
// lua_settable(L, -3);
lua_rawset(L, -3);
}
return 0;
}
}
注意這里的lua_rawset和lua_settable是等價的,只不過lua_rawset速度更快。 最后,我們在加載完Lua腳本以后調用這兩個函數:
復制代碼 代碼如下:
writeLuaArray(L);
readLuaArray(L);
輸出結果為:
復制代碼 代碼如下:
readLuaArray: hehe1
readLuaArray: hehe2
readLuaArray: hehe3
readLuaArray: hehe4
專門的數組操作方法
因為數組一般在程序語言里面都會被特殊對待,Lua也不例外,它的C API還提供另外一種更方便高效地方法來存取數組的元素。
復制代碼 代碼如下:
void lua_rawgeti (lua_State *L, int index, int key);
void lua_rawseti (lua_State *L, int index, int key);
這兩個函數后面兩個參數的意思分別是:index(table在棧中的索引),key(table中數組的索引,下標從1開始) 接下來,我會通過改造上面的示例來演示這兩個API的用法。
讀取數組
因為lua_rawgeti(L,t,key)等價于:
復制代碼 代碼如下:
lua_pushnumber(L, key);
lua_rawget(L, t);
因此,我們的讀取代碼可以改寫成下面這樣:
復制代碼 代碼如下:
void readLuaArray(lua_State *L)
{
lua_getglobal(L, "array");
int n = luaL_len(L, -1);
for (int i = 1; i = n; ++i) {
lua_rawgeti(L, 1, i);
cout"readLuaArray: "lua_tostring(L, -1)endl;
lua_pop(L, 1);
}
}
修改數組
同理,lua_rawset(L,t,key)等價于
復制代碼 代碼如下:
lua_pushnumber(L,key); //此時的棧 table->value->key
lua_insert(L,-2); //調用完后的棧: table->key->value (table[key]=value)
lua_rawset(L,t);
相應的修改數組的代碼可以修改為:
復制代碼 代碼如下:
int writeLuaArray(lua_State *L)
{
lua_settop(L, 0);
lua_getglobal(L, "array");
//確保第一個函數一個要是一個table
luaL_checktype(L, 1, LUA_TTABLE);
int n = luaL_len(L,1);
for (int i = 1; i = n; ++i) {
char buf[256];
sprintf(buf, "hehe%d", i);
lua_pushstring(L, buf);
lua_rawseti(L, 1, i);
}
return 0;
}
C/C++操作Lua字符串
基本字符串操作
Lua C API操作字符串主要包含兩個操作:求子串(lua_pushlstring)和字符串拼接(lua_concat). 例如,我們求一個字符串s的子串[i,j],它可以表示為:
復制代碼 代碼如下:
lua_pushlstring(L, s + i, j - i + 1);
而lua_concat(L,n)則可以把當前棧頂的n個元素轉換成字符串并拼接起來,最后把結果壓入棧頂。 比如,我們想定義一個函數mycontact(…,n)可以把n個字符串拼接起來,n表示字符串的個數,那么我們的代碼可以寫成這樣:
復制代碼 代碼如下:
static int l_mycontact(lua_State* L){
luaL_checktype(L, -1, LUA_TNUMBER);
int n = lua_tonumber(L, -1);
lua_pop(L, 1);
lua_concat(L, n);
return 1;
}
然后,我們需要注冊此函數到libs中去,最后在Lua里面調用此函數:
復制代碼 代碼如下:
print(mylib.mycontact("zilong","shanren"," meng meng"," da",4))
輸出結果為:
復制代碼 代碼如下:
zilongshanren meng meng da
格式化輸出
當我們想要往Lua里面寫入一個格式化字符串時,可以使用函數
復制代碼 代碼如下:
const char *lua_pushfstring (lua_State *L, const char *fmt, ...);
另外,我們還可以使用luaL_Buffer,下面是PIL書中的示例,把Lua字符串轉換成大寫:
復制代碼 代碼如下:
static int str_upper (lua_State *L) {
size_t l;
size_t i;
luaL_Buffer b;
const char *s = luaL_checklstring(L, 1, l); //從Lua棧中取出字符串
char *p = luaL_buffinitsize(L, b, l); //分配一塊與取出字符串同樣大小的緩沖區
for (i = 0; i l; i++)
p[i] = toupper(uchar(s[i]));
luaL_pushresultsize(b, l); //把緩沖區結果轉換為字符串
return 1;
}
更多的Lua Buffer操作函數如下:
復制代碼 代碼如下:
void luaL_buffinit (lua_State *L, luaL_Buffer *B);
void luaL_addvalue (luaL_Buffer *B);
void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l);
void luaL_addstring (luaL_Buffer *B, const char *s);
void luaL_addchar (luaL_Buffer *B, char c);
void luaL_pushresult (luaL_Buffer *B);
關于每一個函數的用法和每一個參數的含義,大家可以去Lua的Reference Manual上去查看,本文就不贅述了。
存儲Lua狀態
在C函數里面,當我們需要保存函數里面的一些狀態的時候,我們一般采用全局變量或者靜態變量的方式。但是,如果在與Lua交互時,這兩種方法都不可取。 原因有二: 1. C變量很難存儲各種各樣的Lua變量。 2. 當存在多個Lua棧的時候,就不生效了。 在Lua里面有兩種方法來存在函數內的non-local數據:registry和upvalue.
Registry方式
Register是一個Lua的全局Table,只有在Lua的C API里面可以訪問這個Table。它可以用來存儲多個Lua模塊之間的數據。 訪問Register的方式一般為:
復制代碼 代碼如下:
lua_getfield(L, LUA_REGISTRYINDEX, "Key");
我們需要提供一個LUA_REGISTRYINDEX的“偽索引”來標識它在Lua棧中的位置。我們在操作這個table的時候,最好是使用字符串做為key,而不要使用數字來做為key。關于Registry更為實際的用法,我們會在下一篇文章中討論。
Upvalue方式
Upvalue主要用來存儲模塊或者函數內部的一些私有的數據,它與C語言的靜態變量有點類似。具體的用法可以參考PIL
您可能感興趣的文章:- 淺談C/C++中指針和數組的不同
- C++小知識:C/C++中不要按值傳遞數組
- C/C++中接收return返回來的數組元素方法示例
- C/C++ 動態數組的創建的實例詳解
- C/C++ 數組和指針及引用的區別
- 圖文詳解c/c++中的多級指針與多維數組
- 淺析C/C++,Java,PHP,JavaScript,Json數組、對象賦值時最后一個元素后面是否可以帶逗號
- C/C++中獲取數組長度的方法示例
- 淺析C語言編程中的數組越界問題
- C/C++ 避免數組越界的方法