原理是:遍历所有的子表创建对应的元表,hook写入操作(__newindex).
-- 设置table只读 出现改写会抛出lua error
-- 用法 local tb = ConstTable(cfg)
-- 增加了防重置设置read_only的机制
-- lua5.3支持
-- 1)table库支持调用元方法,所以table.remove table.insert 也会抛出错误,
-- 2)不用定义__ipairs 5.3 ipairs迭代器支持访问元方法__index,pairs迭代器next不支持故需要元方法__pairs
-- 低版本lua此函数不能完全按照预期工作
function ConstTable(inputTable)
--正式
if not DEBUG then
return inputTable
end
--测试模式
local travelledTables = {}
local function __readOnly(tbl)
if not travelledTables[tbl] then
local proxy = {}
local proxy_mt = {
__index = tbl,
__newindex = function (t, k, v) error("write err:(ConstTable) can not be writed : "..k,2) end,
__pairs = function (t) return pairs(tbl) end,
__len = function (t) return #tbl end,
}
setmetatable(proxy, proxy_mt)
travelledTables[tbl] = proxy
--递归
for k, v in pairs(tbl) do
if type(v) == "table" then
tbl[k] = __readOnly(v)
end
end
end
return travelledTables[tbl]
end
return __readOnly(inputTable)
end
对于Lua5.1需要添加pairs,ipairs读取元表支持
lbaselib.c
static int pairsmeta (lua_State *L, const char *method, int iszero,
lua_CFunction iter) {
luaL_checkany(L, 1);
if (luaL_getmetafield(L, 1, method) == LUA_TNIL) { /* no metamethod? */
lua_pushcfunction(L, iter); /* will return generator, */
lua_pushvalue(L, 1); /* state, */
if (iszero) lua_pushinteger(L, 0); /* and initial value */
else lua_pushnil(L);
}
else {
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
lua_call(L, 1, 3); /* get 3 values from metamethod */
}
return 3;
}
static int luaB_pairs (lua_State *L) {
return pairsmeta(L, "__pairs", 0, luaB_next);
}
static int luaB_ipairs (lua_State *L) {
return pairsmeta(L, "__ipairs", 1, ipairsaux);
}
同时lua层添加__ipairs方法
local proxy_mt = {
__index = tbl,
__newindex = function (t, k, v) error("write err:(ConstTable) can not be writed : "..k,2) end,
__pairs = function (t) return pairs(tbl) end,
__ipairs = function (t) return ipairs(tbl) end,
__len = function (t) return #tbl end,
}