用户数据环境示例 |
|
这是基本的用户数据结构
// 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 索引 2 处找到对象的元表(我在所有方法中使用 upvalue 1 作为元表)
/* 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
元方法,这意味着最终的查找只需要进行一次,并且还允许在 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
函数的第 2 个上值附加(为了查找速度)。如果在该表中找不到,我们只返回 nil
lua_gettable(L, lua_upvalueindex(2)); return 1; }
__newindex
方法的逻辑略有不同。同样,第一个检查是针对数字和短字符串键,但在这种情况下,对于错误的数字键和错误的值都会抛出错误。(对 luaL_checknumber
的调用是不正确的,我当时很懒:它会在某个时候被修复。它会产生难以理解的错误消息。)如果失败,则在必要时创建环境表,并将键值对插入新创建的表中。(如上所述,我通常会在创建新的环境表时将方法表作为 __index
元附加。)
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; }
__index
和 __newindex
元方法的方法,它可以很好地处理元表,并且显然是按需/延迟构建的。它受到 5.1 如何允许在 C 函数中使用单个环境表的影响。5.0/5.1 兼容性的既定目标可能在早期提供。大量使用括号 () 会降低可读性。这可以放在 Lua Fu 的 LuaDirectory 中。我认为那里有一个地方可以称为设计模式。--DavidManura