Metamethod de recherche par le biais de __index?
-
17-09-2020 - |
Question
J'ai mis en place mon propre système de classe et j'ai de la difficulté avec __tostring
;Je soupçonne un problème similaire peut se produire avec d'autres metamethods, mais je n'ai pas essayé.
(Bref détour:chaque classe a un __classDict
attribut, la tenue de toutes les méthodes.Il est utilisé comme la classe des instances __index
.Dans le même temps, l' __classDict de l' __index
est la super-classe' __classDict
, afin que les méthodes des super-classes sont authomatically leva les yeux.)
Je voulais avoir un "défaut tostring" comportement dans tous les cas.Mais il ne fonctionne pas:le "tostring" le comportement n'est pas la "propagation" à travers les sous-classes correctement.
J'ai fait ce test illustrer mon problème:
mt1 = {__tostring=function(x) return x.name or "no name" end }
mt2 = {}
setmetatable(mt2, {__index=mt1})
x = {name='x'}
y = {name='y'}
setmetatable(x, mt1)
setmetatable(y, mt2)
print(x) -- prints "x"
print(mt2.__tostring(y)) -- prints "y"
print(y) -- prints "table: 0x9e84c18" !!
J'aurais plutôt que la dernière ligne d'impression "y".
Arus "to_String" comportement doit être à l'aide de l'équivalent de
rawget(instance.class.__classDict, '__tostring')
au lieu de faire l'équivalent de
instance.class.__classDict.__tostring
Je soupçonne la même chose se produit avec tous les metamethods; rawget
-des opérations équivalentes sont utilisées.
Je suppose que une seule chose que je puisse faire est de copier tous les metamethods quand je fais mes sous-classement (l'équivalent sur l'exemple ci-dessus serait de faire mt2.__tostring = mt1.__tostring
), mais c'est à peu élégante.
Quelqu'un a combattu avec ce genre de problème?Quels sont vos solutions?
La solution 2
Grâce à daurnimator les commentaires, je crois que j'ai trouvé un moyen de faire metamethods "suivre" __index
comme je voudrais.C'est condensée sur cette fonction:
local metamethods = {
'__add', '__sub', '__mul', '__div', '__mod', '__pow', '__unm', '__concat',
'__len', '__eq', '__lt', '__le', '__call', '__gc', '__tostring', '__newindex'
}
function setindirectmetatable(t, mt)
for _,m in ipairs(metamethods) do
rawset(mt, m, rawget(mt,m) or function(...)
local supermt = getmetatable(mt) or {}
local index = supermt.__index
if(type(index)=='function') then return index(t,m)(...) end
if(type(index)=='table') then return index[m](...) end
return nil
end)
end
return setmetatable(t, mt)
end
J'espère que c'est assez simple.Lorsqu'un nouveau métatable est défini, il l'initialise avec tous les metamethods (sans remplacement de ceux existants).Ces metamethods sont prêts à "passer" les demandes de "parent metatables".
C'est la solution la plus simple que j'ai pu trouver.Eh bien, j'ai effectivement trouvé une solution qui a utilisé moins de personnages et était un peu plus rapide, mais il s'agit de magie noire (elle a impliqué métatable fonctions de référence eux-mêmes à l'intérieur de leur propre corps) et c'était beaucoup moins lisible que celui-ci.
Si quelqu'un trouve un plus court, plus simple fonction qui fait la même chose, je serai heureux de lui donner la réponse.
L'utilisation est simple:remplacer setmetatable
par setindirectmetatable
quand vous voulez à "aller":
mt1 = {__tostring=function(x) return x.name or "no name" end }
mt2 = {}
setmetatable(mt2, {__index=mt1})
x = {name='x'}
y = {name='y'}
setmetatable(x, mt1)
setindirectmetatable(y, mt2) -- only change in code
print(x) -- prints "x"
print(mt2.__tostring(y)) -- prints "y"
print(y) -- prints "y"
Un petit mot d'avertissement: setindirectmetatable
crée metamethods sur mt2.Changer ce comportement par conséquent, une copie est faite, et mt2 reste inchangé, devrait être trivial.Mais les laisser définir par défaut est en fait mieux pour mes fins.
Autres conseils
Je soupçonne la même chose se produit avec tous les metamethods;rawget-opérations équivalentes sont utilisées.
Qui est correct.à partir de la lua manuel:
...doit être lu comme
rawget(getmetatable(obj) or {}, event)
.Qui est, l'accès à un metamethod de ne pas se prévaloir d'autres metamethods, et l'accès à des objets sans metatables de ne pas échouer (il simplement des résultats dans le néant).
En général, chaque classe a ses propres métatable, et vous copiez toutes les références à des fonctions.C'est, ne mt2.__tostring = mt1.__tosting
De mon expérience avec Lua 5.1, metamethods sont recherchés dans metatables à l'aide de rawget(), et c'est pourquoi vous doit copie de la référence à la fonction dans toutes les classes de la table que vous créez.
Voir la L'Héritage Tutoriel sur le Lua Utilisateurs de Wiki.