2017-05-22 1 views
1

Je voudrais créer MyClass classe dans Lua dans un fichier séparé myclass.lua que je peux importer et utiliser plus tard. Il devrait fonctionner de la façon suivante:Comment créer une classe importable simple dans Lua?

local MyClass = require 'myclass' 
tab = {1,2,3} 
m = MyClass(tab) 

Cependant, après le code Lua docs Je ne peux pas le faire fonctionner et me fais des erreurs attempt to call global 'MyClass' (a table value).

Le code que je l'ai écrit jusqu'à présent pour myclass.lua:

local MyClass = {} 
MyClass.__index = MyClass 

function MyClass.__init(tab) 
    self.tab = tab or {} 
    setmetatable({},MyClass) 
    return self 
end 
return MyClass 

Il existe une pléthore d'exemples comment écrire des classes dans Lua, mais je ne pense pas que je comprends la différence et comme résultat se perdre dans les détails de l'implémentation. Y a-t-il plus ou moins conventionnel façon de le faire?

Répondre

1

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:

  1. Vous essayez d'utiliser __init au lieu de __call. Lua n'a pas de métaméthode __init.
  2. __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.
  3. 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.

+0

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. –

0

Il n'y a pas __init métaméthode disponible pour une table. Lorsque vous effectuez les opérations suivantes:

m = MyClass(tab) 

il recherche la définition de la méthode MyClass.__call. Il suffit de mettre à jour votre myclass.lua comme:

local MyClass = {} 
MyClass.__index = MyClass 

function MyClass:__call(tab) 
    self.tab = tab or {} 
    setmetatable({},MyClass) 
    return self 
end 

return MyClass 
+0

Oui, mais cela ne résout pas le problème en cas d'erreur. Essayez d'importer ceci et appelez 'local m = MyClass ({1,2,3})'. – minerals

+2

@minerals Le premier argument de '__call' est la" fonction "qui a été appelée - dans ce cas, la classe. Alors changez-le en 'function MyClass .__ call (_, tab)' ici. (Si vous avez des constructeurs "génériques" par la suite, vous pouvez donner un nom différent à '' _' 'et l'utiliser pour savoir dans quelle classe '__call' devrait construire une instance.) – nobody

+0

Cela ne fonctionne pas. J'obtiens des tentatives de tentative d'appel des erreurs globales de MyClass. – minerals