Chain Lua metatables

时间:2011-11-13 05:22:33

标签: lua

我的情况是两个库L,M试图为_G设置元表(分别命名为mL,mM)。元数据中唯一的东西是__index。

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

3 个答案:

答案 0 :(得分:2)

有一个metatable存储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 :(得分:1)

假设你的代码可以摆弄_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

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

[请注意,由于两个库元表都已修改,因此无论最后安装哪个(“赢得”竞争对手)都可以使用。]

答案 2 :(得分:1)

使用__metatable为它们提供一个实际上不是metatable的表,或者为库提供不同的setmetatable:这样他们就无法更改_G metatable。

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

OR

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

(取决于图书馆的工作方式)

如果您仍想跟踪它对metatable所做的事情;在返回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

否则这对于图书馆来说是非常糟糕的做法;告诉作者不要!