Цепочка Lua Metatables - PullRequest
       1

Цепочка Lua Metatables

4 голосов
/ 13 ноября 2011

У меня есть ситуация, когда две библиотеки L, M пытаются установить метатабельный для _G (названный mL, mM соответственно). В метатаблицах есть только __index.

Как я могу связать эти две метатаблицы так, чтобы, если __index в одном не сработал, он вызывал индекс в другом?

Ответы [ 3 ]

2 голосов
/ 13 ноября 2011

Имейте одну метатаблицу, которая хранит как mL, так и mM, а если одна возвращает 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 голос
/ 13 ноября 2011

Используйте __metatable, чтобы дать им таблицу, которая на самом деле не является метатабельной, или дать библиотеке другую setmetatable: таким образом, они не смогут изменить ваш _G ​​метатабельный.

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

(в зависимости от того, как работает библиотека)

Если вы все еще хотите отследить, что он делает с метатабелем; просмотрите mt перед возвращением ob (и если вы действительно хотите связать поиск __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

В противном случае это очень плохая практика для библиотек; скажите автору, чтобы не!

1 голос
/ 13 ноября 2011

Предполагая, что есть момент, когда ваш код может возиться с метатабельным _G сам, после того, как библиотеки взломали, чтобы исправить то, что сделали L и M, вы можете просто вставить свой собственный метатаблицу, которая выполняет комбинированный поиск,Например:

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

setmetatable (_G, combined_metatable)

Преимущество заключается в том, что вы не можете играть с mL или mM.

Если у вас нет , есть возможность исправитьпосле факта, вы можете просто изменить __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

[Обратите внимание, что при изменении обеих метатаблиц библиотеки это будет работать в зависимости от того, что будет установлено последним(«Победа» в конкурсе).]

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...