Dans Lua, vous ne pouvez pas appeler habituellement une table comme vous le feriez appeler une fonction. Par exemple, ce code produira une erreur de «tentative d'appel local 't' (une valeur de table)».
local t = {}
t()
Cependant, il existe un moyen de faire ce travail en utilisant des métabalises.
local hello = {}
local mt = {} -- The metatable
mt.__call = function()
print("Hello!")
end
setmetatable(hello, mt)
hello() -- prints "Hello!"
Lorsque vous essayez d'appeler une table comme vous le feriez pour une fonction, vérifie d'abord Lua pour voir si la table a une métatable. Si c'est le cas, il essaie d'appeler la fonction dans la propriété __call
de cette métabalise. Le premier argument de la fonction __call
est la table elle-même, et les arguments suivants sont les arguments qui ont été transmis lorsque la table a été appelée en tant que fonction. Si la table n'a pas de metable, ou si la metable n'a pas de fonction __call
, une erreur "tentative d'appel local 't" est levée.
Votre code exemple a trois problèmes:
- Vous essayez d'utiliser
__init
au lieu de __call
. Lua n'a pas de métaméthode __init
.
__call
prend différents paramètres que ceux que vous utilisez. Le premier paramètre de la fonction __call
est la table elle-même. Vous pouvez utiliser function MyClass.__call(self, tab)
ou utiliser la syntaxe deux-points function MyClass:__call(tab)
, qui ajoute implicitement le paramètre self
pour vous. Ces deux syntaxes sont fonctionnellement identiques.
- Vous n'avez pas défini de métabalit pour la table
MyClass
. Bien que vous définissiez une méta-donnée pour les objets de MyClass, cela ne signifie pas qu'un metatable est automatiquement défini pour MyClass lui-même.
Pour résoudre ce problème, vous pouvez faire quelque chose comme ce qui suit:
local MyClass = {}
setmetatable(MyClass, MyClass)
MyClass.__index = MyClass
function MyClass:__call(tab)
local obj = {}
obj.tab = tab or {}
setmetatable(obj, MyClass)
return obj
end
return MyClass
Ceci définit MyClass à s'utiliser comme métatable, ce qui est parfaitement valide Lua.
Le système de métadonnées est très flexible, et vous permet d'avoir à peu près n'importe quel schéma de classe/objet que vous voulez. Par exemple, si vous le souhaitez, vous pouvez tout faire en ligne.
local MyClass = {}
setmetatable(MyClass, {
__call = function (class, tab)
local obj = {}
obj.tab = tab or {}
setmetatable(obj, {
__index = MyClass
})
return obj
end
})
return MyClass
En plus d'être concis, cela a aussi l'avantage que les gens ne peuvent pas changer les métaméthodes de la classe si elles ont accès à la table de classe.
C'est un léger inconvénient (ok, un inconvénient majeur) avec la mise en méta d'une table comme elle-même, et en définissant son metamethod __index à lui-même. 'obj local = MyClass {1, 2, 3}' fonctionne réellement bien avec ce code, mais si vous essayez d'indexer 'obj.foo', alors il voit que' foo' n'existe pas dans 'obj', et puis vérifie la table 'MyClass' pour' foo' car c'est ce que vous définissez l'index __ de l'obj. Il ne trouve pas 'foo' dans' MyClass' non plus, donc il ressemble à '__index 'de MyClass', qui est ...' MyClass'. Lua est assez intelligent pour détecter qu'il s'agit d'une boucle infinie, donc il vous fait des erreurs. –