Lua 只读表ConstTable实现

248 阅读1分钟

原理是:遍历所有的子表创建对应的元表,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,
}