Lua metatables chain

I have a situation where two libraries L, M, are trying to set the meta-furniture for _G (with the name ml, mM respectively). The only thing in the meta tags is __index.

How can I bind these two meta so that if __index in one fails, it calls the index in the other?

+4
source share
3 answers

There is one metatet that stores both mL and mM , and if one returns nil , check the other:

 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
source

Assuming where your code can script with _G metatable, after the libraries have pounced to fix what L and M did, you can simply insert your own metatet that does a combined search, for example:

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

This has the advantage that you do not play with mL or mM yourself.

If you don’t have the opportunity to rectify the situation after the fact, you can simply change the __index entries of the library __index tags to perform a combined search:

 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 

[Please note that since both library metadata are changed, this will work depending on which one is set last (β€œwin”).]

+1
source

Use __metatable to give them a table that is actually not a metatheme, or to give the library another setmetatable method: that way they cannot change your _G meta tags.

 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 

(depending on how the library does something)

If you still want to track what it does with the metathema; look at mt before returning ob (and if you really want to link the __index search add to the table):

 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 

Otherwise, this is a very bad practice for libraries; tell the author not!

+1
source

Source: https://habr.com/ru/post/1380968/


All Articles