首页 文章

Chain Lua metatables

提问于
浏览
4

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

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

3 回答

  • 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

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

    [请注意,由于两个库元表都被修改,这将适用于最后安装的任何一个(“赢得”竞争) .

  • 1

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

    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
    

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

    如果你仍然想跟踪它对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
    

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

相关问题