链式Lua元表

我有一个这样的情况:两个库 L 和 M 都试图为 _G 设置元表(分别命名为 mL 和 mM)。元表中唯一的内容是 __index。

如何将这两个元表链接起来,以便如果一个中的 __index 失败,则调用另一个中的索引?

原文链接 https://stackoverflow.com/questions/8109790

点赞
stackoverflow用户734069
stackoverflow用户734069

mLmM 都存储在一个 metatable 中,如果一个返回nil,就检查另一个:

local metatbl = {}
metatbl.tbls = {mL, mM};
function metatbl.__index(intbl, key)
  for i, mtbl in ipairs(metatbl.tbls) do
    local mmethod = mtbl.__index
    if(type(mmethod) == "function") then
      local ret = mmethod(table, key)
      if ret then return ret end
    else
     if mmethod[key] then return mmethod[key] end
    end
  return nil
  end
end

setmetatable(_G,metatbl)
2011-11-13 05:47:54
stackoverflow用户284969
stackoverflow用户284969

假设在库文件改变了 _G 的 metatable 后,你的代码可以直接修改 _G 的 metatable 本身,来修复 L 和 M 造成的问题,你可以插入自己的 metatable 来进行联合搜索,例如:

combined_metatable = {
   __index = function (t, k)
                return mL.__index (t, k) or mM.__index (t, k)
             end
}

setmetatable (_G, combined_metatable)

这样做的好处是不必改动 mLmM

如果你没有机会在事后进行更正,也可以修改库 metatable 的 __index 条目来进行联合搜索:

local original_mM_index = mM.__index
local original_mL_index = mL.__index

local function L_then_M_index (t, k)
   return original_mL_index (t, k) or original_mM_index (t, k)
end

mL.__index = L_then_M_index
mM.__index = L_then_M_index

[需要注意的是,由于不能修改库 metatable 的顺序,无论哪个库最后被安装,这种方法都能够正常工作。]

2011-11-13 06:05:24
stackoverflow用户282536
stackoverflow用户282536

使用 __metatable 给它们一个不是真正元表的表格,或者给库一个不同的 setmetatable:这样他们就无法更改你的_G元表。

getmetatable(getfenv()).__metatable = function ( o ) return { } end

或者

local orig_setmetatable = setmetatable
function setmetatable ( ob , mt )
    if ob == getfenv() or ob == _G then
        return ob
    else
        return orig_setmetatable(ob,mt)
    end
end

(取决于库是如何进行操作的)

如果您仍然想要跟踪它对元表所做的事情,请在返回 ob 之前查看 mt(如果您实际上想要将 __index 查找链接起来,请添加到表中):

local env_indexes = {}
setmetatable(_G,{__index=function(t,k) for i,m in ipairs(env_indexes) do local v=m[k]; if v then return v end end return nil end } )
local orig_setmetatable = setmetatable
function setmetatable ( ob , mt )
    if ob == _G then
        table.insert ( env_indexes , mt.__index )
        return ob
    else
        return orig_setmetatable(ob,mt)
    end
end

否则,这种做法对于库来说非常不好,告诉作者不要这样做!

2011-11-13 06:55:48