【问题标题】:Chain Lua metatables链 Lua 元表
【发布时间】:2011-11-13 15:07:41
【问题描述】:

我遇到两个库 L、M 正在尝试为 _G 设置元表(分别命名为 mL、mM)的情况。元表中唯一的东西是 __index。

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

【问题讨论】:

    标签: lua


    【解决方案1】:

    有一个存储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)
    

    【讨论】:

      【解决方案2】:

      假设您的代码在某个时刻可以摆弄_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
      

      [请注意,由于两个库元表都已修改,因此无论最后安装哪个都有效(“赢得”比赛)。]

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-07-30
          • 2018-06-01
          • 2022-06-14
          • 2013-05-05
          • 2018-11-14
          • 2012-03-15
          • 2012-11-04
          • 2011-12-31
          相关资源
          最近更新 更多