用户数据环境示例 |
|
这是基本的用户数据结构
// 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