链式 Lua 元表

4

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

我如何链接这两个元表,以便如果一个__index失败,则调用另一个__index呢?

3个回答

2
拥有一个元表来存储 mLmM,如果返回 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)

1
假设在库文件已经搞砸了之后,您的代码可以直接操作_G的元表本身来修复L和M所造成的问题,那么您只需要插入自己的元表即可进行组合搜索,例如:
combined_metatable = {
   __index = function (t, k)
                return mL.__index (t, k) or mM.__index (t, k)
             end
}

setmetatable (_G, combined_metatable)

这样做的好处是不必直接操作 mLmM

如果您没有事后更正的机会,可以修改库元表的 __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

[请注意,由于两个库元表都被修改,因此无论哪个安装在最后(“赢得”比赛),这都会起作用。]


1
使用 __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

否则,这对于库来说是非常糟糕的做法;告诉作者不要这样做!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接