第一版是為 Lua 5.0 編寫的。儘管在很大程度上仍然適用於後續版本,但還是有一些差異。
第四版針對 Lua 5.3,可在 Amazon 和其他書店購買。
購買本書,您還可以 支持 Lua 專案。
![]() |
用 Lua 程式設計 | ![]() |
第四部分 C API 第 28 章 C 中的使用者定義類型 |
我們的下一步是將新類型轉換為物件,以便可以使用一般的物件導向語法對其執行個體運算,例如
a = array.new(1000) print(a:size()) --> 1000 a:set(10, 3.4) print(a:get(10)) --> 3.4
請記住,a:size()
等於 a.size(a)
。因此,我們必須安排表達式 a.size
傳回我們的 getsize
函式。此處的關鍵機制是 __index
元方法。對於表格,每當 Lua 找不到特定鍵的值時,就會呼叫此元方法。對於使用者資料,每次存取都會呼叫它,因為使用者資料根本沒有鍵。
假設我們執行下列程式碼
local metaarray = getmetatable(array.new(1)) metaarray.__index = metaarray metaarray.set = array.set metaarray.get = array.get metaarray.size = array.size在第一行,我們建立一個陣列,只為了取得其元表格,然後將其指定給
metaarray
。(我們無法從 Lua 設定使用者資料的元表格,但我們可以毫無限制地取得其元表格。)然後,我們將 metaarray.__index
設定為 metaarray
。當我們評估 a.size
時,Lua 無法在物件 a
中找到鍵 "size"
,因為該物件是使用者資料。因此,Lua 會嘗試從 a
的元表格的 __index
欄位取得此值,而該欄位碰巧就是 metaarray
本身。但是 metaarray.size
是 array.size
,因此 a.size(a)
會產生 array.size(a)
,正如我們所要的。
當然,我們可以用 C 編寫相同內容。我們甚至可以做得更好:現在陣列是物件,具有自己的運算,我們不再需要在表格 array
中有這些運算了。我們的函式庫仍然必須匯出的唯一函式是 new
,用於建立新陣列。所有其他運算都只作為方法提供。C 程式碼可以直接將它們註冊為此類方法。
運算 getsize
、getarray
和 setarray
沒有從我們先前的做法中變更。將會變更的是我們如何註冊它們。也就是說,我們必須變更開啟函式庫的函式。首先,我們需要兩個獨立的函式清單,一個是常規函式,一個是方法
static const struct luaL_reg arraylib_f [] = { {"new", newarray}, {NULL, NULL} }; static const struct luaL_reg arraylib_m [] = { {"set", setarray}, {"get", getarray}, {"size", getsize}, {NULL, NULL} };開啟函式庫的函式
luaopen_array
的新版本必須建立元表,將它指定給它自己的 __index
欄位,註冊所有方法在那裡,並建立和填入 array
表格
int luaopen_array (lua_State *L) { luaL_newmetatable(L, "LuaBook.array"); lua_pushstring(L, "__index"); lua_pushvalue(L, -2); /* pushes the metatable */ lua_settable(L, -3); /* metatable.__index = metatable */ luaL_openlib(L, NULL, arraylib_m, 0); luaL_openlib(L, "array", arraylib_f, 0); return 1; }在此我們使用
luaL_openlib
的另一個功能。在第一次呼叫時,當我們傳遞 NULL
作為函式庫名稱時,luaL_openlib
沒有建立任何表格來封裝函式;相反地,它假設封裝表格在堆疊上,低於任何偶爾的向上值。在此範例中,封裝表格是元表本身,這是 luaL_openlib
將會放置方法的地方。下一次呼叫 luaL_openlib
會正常運作:它建立一個具有給定名稱 (array
) 的新表格,並在那裡註冊給定的函式 (在此情況下只有 new
)。
作為最後的修飾,我們將新增一個 __tostring
方法到我們的類型中,這樣 print(a)
會列印 array
加上括號中的陣列大小 (例如,array(1000)
)。函式本身在此
int array2string (lua_State *L) { NumArray *a = checkarray(L); lua_pushfstring(L, "array(%d)", a->size); return 1; }
lua_pushfstring
函式格式化字串並將它留在堆疊頂端。我們還必須將 array2string
新增到清單 arraylib_m
中,以將它包含在陣列物件的元表中
static const struct luaL_reg arraylib_m [] = { {"__tostring", array2string}, {"set", setarray}, ... };
版權所有 © 2003–2004 Roberto Ierusalimschy。保留所有權利。 | ![]() |