2016-11-03 2 views
2

J'ai lu métatables (here et here) et je me suis demandé si je pouvais créer un proxy d'appel de fonction en ajoutant simplement une fonction du même nom à la table __index, de sorte que il appelle la fonction d'index (la mienne), et alors je peux appeler la fonction normale.Essayer de faire un proxy lua avec métatables

C'est ce que j'ai essayé:

local test = {} 
test.static = function() print("static") end -- The normal function 

local old = getmetatable(test) or {} -- Get the orginal meta 
old.__index = { 
    static = function() print("hook") end -- Adding my function 
} 

test.static() -- call just for test (prints -> static) 
test = setmetatable(test , old) -- setting the metatable 
test.static() -- call just for test (prints -> static, but should be hook) 

Répondre

0

Essayez de donner la official source une lecture, en particulier §2.4 – Metatables and Metamethods, et la description de la __index methamethod, qui se lit comme suit:

  • __index: L'accès à l'indexation table[key]. Cet événement se produit lorsque la table n'est pas une table ou lorsque la clé n'est pas présente dans la table. Le métaméthode est recherché dans le tableau.

    Malgré le nom, le métaméthode de cet événement peut être une fonction ou une table. S'il s'agit d'une fonction, elle est appelée avec table et clé comme arguments, et le résultat de l'appel (ajusté à une valeur) est le résultat de l'opération. S'il s'agit d'une table, le résultat final est le résultat de l'indexation de cette table avec la clé. (Cette indexation est régulière, pas cru, et peut donc déclencher une autre métaméthode.)

Nous pouvons voir que votre pensée est à l'envers. Les propriétés d'une table référencée par le métaméthode __index ne sont recherchées que si la table d'origine ne contient pas la clé.

Si vous voulez 'accrocher' une fonction, vous devrez l'écraser, en sauvegardant éventuellement l'original pour le restaurer plus tard. Si vous voulez coller fonctionnellement à une fonction existante, vous pouvez écrire une petite fonction d'accrochage, qui crée simplement une fermeture autour des fonctions, en les appelant tour à tour.

local function hook (original_fn, new_fn) 
    return function (...) 
     original_fn(...) 
     new_fn(...) 
    end 
end 

local test = {} 
test.foo = function() print('hello') end 
test.foo = hook(test.foo, function() print('world!') end) 

test.foo() --> prints 'hello' then 'world!' 

Vous pouvez basculer entre métatables, en supposant que la table d'origine ne remplace jamais les clés d'intérêt, pour obtenir des résultats différents:

local my_table, default, extra = {}, {}, {} 

function default.foo() print('hello') end 

function extra.foo() print('world!') end 

local function set_mt (t, mt) 
    mt.__index = mt 
    return setmetatable(t, mt) 
end 

set_mt(my_table, default) 
my_table.foo() --> prints 'hello' 
set_mt(my_table, extra) 
my_table.foo() --> prints 'world!'