使用者資料環境範例 |
|
以下為基本使用者資料結構
// How do you get the wiki to syntax colour C? typedef struct lvec_s { #ifdef VERSION51 int has_env; #else int len; #endif lua_Number d[1]; } lvec_t;
has_env
成員的使用。除了在 x86 架構上將使用者資料標頭填補為雙字邊界(避免雙倍精度浮點數跨越快取邊界)之外,這也會用作旗標,表示物件是否有有用的環境資料表。(在版本 5.0 中,len
成員會引發定位錯誤。最好使用 union {int len; lua_Number dummy} u;
來取代。)Lua API 不允許建立沒有環境資料表的使用者資料,而我找到的所有存在性檢查其他解法都比較慢。
以下是 getter 方法,預期在 upvalue index 2 找到物件的方法資料表(我在所有方法中都使用 upvalue 1 作為 meta 資料表)
/* getter and setter, allowing overriding in 5.1. 5.0 doesn't have environment tables */ static int lvec_meta_index (lua_State *L) { int len = 0; lvec_t *u = getveclen(L, 1, &len);
if (lua_type(L, 2) == LUA_TNUMBER) { int idx = checkkey(L, len); if (idx) { lua_pushnumber(L, u->d[idx-1]); return 1; } else return 0; }
if (lua_type(L, 2) == LUA_TSTRING) { size_t slen; const char *key = lua_tolstring(L, 2, &slen); if (slen == 1) { int idx = key[0] - 'x'; if (len <= 3 && idx >= 0 && idx < len) { lua_pushnumber(L, u->d[idx]); return 1; } } }
__index
meta 方法,一旦建立環境資料表,表示最後一次的搜尋只能進行一次,同時也能讓物件在 Lua 中建立子類別。#ifdef VERSION51 if (u->has_env) { lua_getfenv(L, 1); lua_pushvalue(L, 2); lua_gettable(L, -2); if (!lua_isnil(L, -1)) return 1; lua_pop(L, 2); } #endif
__index
函式(以加速搜尋速度)的執行個體方法資料表中,搜尋金鑰。如果在此資料表中找不到,我們只傳回 nil
lua_gettable(L, lua_upvalueindex(2)); return 1; }
__newindex
方法略有不同的邏輯。同前面,它首先檢查數字鍵和短字串鍵,但此情況中,壞的數字鍵或壞的數字都會拋出錯誤訊息。(呼叫 luaL_checknumber
不正確,我是偷懶:我之後會修正它。它會產生難以解讀的錯誤訊息。)如果失敗,它會在必要時建立一個環境表格,再將鍵值對插入到新的表格。(如前述,我通常在建立新的環境時,會將方法表格附加一個 __index
meta。)
static int lvec_meta_newindex (lua_State *L) { int len = 0; lvec_t *u = getveclen(L, 1, &len); if (lua_type(L, 2) == LUA_TNUMBER) { int idx = checkkey(L, len); if (idx) u->d[idx-1] = luaL_checknumber(L, 3); else luaL_error(L, "Vector index not integer or out of range"); return 0; } if (lua_type(L, 2) == LUA_TSTRING) { size_t slen; const char *key = lua_tolstring(L, 2, &slen); if (slen == 1) { int idx = key[0] - 'x'; if (len <= 3 && idx >= 0 && idx < len) { u->d[idx] = luaL_checknumber(L, 3); return 0; } } } #ifdef VERSION51 if (!u->has_env) { lua_newtable(L); lua_pushvalue(L, -1); lua_setfenv(L, 1); u->has_env = 1; } else lua_getfenv(L, 1); lua_replace(L, 1); lua_settable(L, 1); #else else luaL_error(L, "Vector index not integer or out of range"); #endif return 0; }