How to iterate over a table modified with luaL_ref and luaL_unref?
我正在使用Lua的C API扩展Lua。在我的模块中,我想使用
由于
考虑以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | /* tableDump prints a table: */ /* key: value, key: value, ... */ lua_newtable(L); lua_pushboolean(L, 0); int ref1 = luaL_ref(L, -2); lua_pushinteger(L, 7); int ref2 = luaL_ref(L, -2); lua_pushstring(L,"test"); int ref3 = luaL_ref(L, -2); tableDump(L, -1); luaL_unref(L, -1, ref1); tableDump(L, -1); luaL_unref(L, -1, ref3); tableDump(L, -1); luaL_unref(L, -1, ref2); tableDump(L, -1); printf("done.\ "); |
输出:
1 2 3 4 5 | 1: false, 2: 7, 3: `test', 3: `test', 2: 7, 0: 1, 3: 1, 2: 7, 0: 3, 3: 1, 2: 3, 0: 2, done. |
这里发生了什么?我该如何解决?是否有一些技巧可以遍历引用并忽略未引用?我必须停止使用
编辑
首先,谢谢您的回复!
也许我问错了问题。
请允许我更具体一点。我有一个客户端用户数据,它需要管理许多订阅用户数据。订阅是通过客户端的订阅方法创建的。订阅通过客户端的unsubscribe方法删除。订阅用户数据基本上是实现细节,因此它们不会在客户端API中公开。相反,客户端API使用订阅引用,因此使用
1 2 | ref = client:sub(channel, func) cleint:unsub(ref) |
抓住了。我希望客户端自动取消订阅__gc上的所有剩余订阅(否则用户将收到段错误)。所以看来我需要遍历订阅。我真的在这里滥用API吗?有一个更好的方法吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | LUALIB_API int luaL_ref (lua_State *L, int t) { int ref; t = abs_index(L, t); if (lua_isnil(L, -1)) { lua_pop(L, 1); /* remove from stack */ return LUA_REFNIL; /* 'nil' has a unique fixed reference */ } lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ lua_pop(L, 1); /* remove it from stack */ if (ref != 0) { /* any free element? */ lua_rawgeti(L, t, ref); /* remove it from list */ lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ } else { /* no free elements */ ref = (int)lua_objlen(L, t); ref++; /* create new reference */ } lua_rawseti(L, t, ref); return ref; } LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { if (ref >= 0) { t = abs_index(L, t); lua_rawgeti(L, t, FREELIST_REF); lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ lua_pushinteger(L, ref); lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ } } |
这些功能非常聪明,因为它们不需要额外的存储(除了要操作的表之外)。但是,有时不希望将自由参考列表与参考值存储在同一表中,例如在有必要遍历参考值时。
我写了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | static int abs_index(lua_State * L, int i){ return i > 0 || i <= LUA_REGISTRYINDEX ? i : lua_gettop(L) + i + 1; } LUALIB_API int luaX_ref(lua_State *L, int t, int l){ int ref; t = abs_index(L, t); l = abs_index(L, l); if(lua_isnil(L, -1)){ lua_pop(L, 1); /* remove from stack */ return LUA_REFNIL; /* 'nil' has a unique fixed reference */ } lua_rawgeti(L, l, FREELIST_REF); /* get first free element */ ref = (int) lua_tointeger(L, -1); /* ref = l[FREELIST_REF] */ lua_pop(L, 1); /* remove it from stack */ if(ref != 0){ /* any free element? */ lua_rawgeti(L, l, ref); /* remove it from list */ lua_rawseti(L, l, FREELIST_REF); /* (l[FREELIST_REF] = l[ref]) */ }else{ /* no free elements */ ref = (int)lua_objlen(L, l); ref++; /* create new reference */ } lua_pushboolean(L, 1); lua_rawseti(L, l, ref); /* l[ref] = true */ lua_rawseti(L, t, ref); /* t[ref] = value */ return ref; } LUALIB_API void luaX_unref(lua_State *L, int t, int l, int ref){ if(ref >= 0){ t = abs_index(L, t); l = abs_index(L, l); lua_rawgeti(L, l, FREELIST_REF); lua_rawseti(L, l, ref); /* l[ref] = l[FREELIST_REF] */ lua_pushinteger(L, ref); lua_rawseti(L, l, FREELIST_REF); /* l[FREELIST_REF] = ref */ lua_pushnil(L); lua_rawseti(L, t, ref); /* t[ref] = nil */ } } |
现在查看用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | lua_newtable(L); /* 1 */ lua_newtable(L); /* 2 */ lua_pushboolean(L, 0); int ref1 = luaX_ref(L, 1, 2); lua_pushinteger(L, 7); int ref2 = luaX_ref(L, 1, 2); lua_pushstring(L,"test"); int ref3 = luaX_ref(L, 1, 2); tableDump(L, 1); tableDump(L, 2); luaX_unref(L, 1, 2, ref1); tableDump(L, 1); tableDump(L, 2); luaX_unref(L, 1, 2, ref3); tableDump(L, 1); tableDump(L, 2); luaX_unref(L, 1, 2, ref2); tableDump(L, 1); tableDump(L, 2); printf("done.\ "); |
输出:
1 2 3 4 5 6 7 8 9 | 1: false, 2: 7, 3: `test', 1: true, 2: true, 3: true, 2: 7, 3: `test', 3: true, 2: true, 0: 1, 2: 7, 3: 1, 2: true, 0: 3, 3: 1, 2: 3, 0: 2, done. |
为了"确保键返回的唯一性",
如果您要继续按照Nicol的观察来"滥用API",并依赖于实现定义的行为,则可以按照此链接列表查看在迭代表时键是否为已删除的引用。为了避免依赖于实现定义的行为并提高性能,可以保留一个单独的已删除引用表,并在迭代该表时跳过它们,尽管您需要忽略
如果您确实需要迭代引用,则最好完全使用其他机制。您可以简单地将所有引用/值对放在单独的表中,并在删除引用时将单独表中的值设置为