Recherche…


Syntaxe

  • [[local] mt =] getmetatable ( t ) -> récupère la métatable associée pour ' t '
  • [[local] t =] setmetatable ( t , mt ) -> définit la métatable pour ' t ' en ' mt ' et renvoie ' t '

Paramètres

Paramètre Détails
t Variable faisant référence à une table lua; peut aussi être un littéral de table.
mt Tableau à utiliser comme métatable; peut avoir zéro ou plusieurs champs de métaméthode définis.

Remarques

Il y a quelques méthodes non mentionnées ici. Pour la liste complète et leur utilisation, voir l'entrée correspondante dans le manuel de lua .

Création et utilisation de métabalises

Un métatable définit un ensemble d'opérations qui modifient le comportement d'un objet lua. Un métatable est juste une table ordinaire, qui est utilisée d'une manière spéciale.

local meta = { } -- create a table for use as metatable

-- a metatable can change the behaviour of many things
-- here we modify the 'tostring' operation:
-- this fields should be a function with one argument.
-- it gets called with the respective object and should return a string
meta.__tostring = function (object)
    return string.format("{ %d, %d }", object.x, object.y)
end

-- create an object
local point = { x = 13, y = -2 }
-- set the metatable
setmetatable(point, meta)

-- since 'print' calls 'tostring', we can use it directly:
print(point) -- prints '{ 13, -2 }'

Utiliser des tables comme méthodes

Certaines méthodes ne doivent pas nécessairement être des fonctions. L'exemple le plus important pour cela est la __index métadonnées __index. Il peut également s'agir d'une table, qui est ensuite utilisée comme référence. Ceci est assez couramment utilisé dans la création de classes en lua. Ici, une table (souvent la métatable elle-même) est utilisée pour contenir toutes les opérations (méthodes) de la classe:

local meta = {}
-- set the __index method to the metatable.
-- Note that this can't be done in the constructor!
meta.__index = meta

function create_new(name)
    local self = { name = name }
    setmetatable(self, meta)
    return self
end

-- define a print function, which is stored in the metatable
function meta.print(self)
    print(self.name)
end

local obj = create_new("Hello from object")
obj:print()

Garbage Collector - la metamethod __gc

5.2

Les objets en lua sont des ordures collectées. Parfois, vous devez libérer des ressources, imprimer un message ou faire autre chose lorsqu'un objet est détruit (collecté). Pour cela, vous pouvez utiliser la __gc __gc, qui est appelée avec l'objet comme argument lorsque l'objet est détruit. Vous pouvez voir cette méthode comme une sorte de destructeur.

Cet exemple montre la __gc __gc en action. Lorsque la table interne affectée à t reçoit des ordures, elle imprime un message avant d'être collecté. De même pour la table externe à la fin du script:

local meta =
{
    __gc = function(self)
        print("destroying self: " .. self.name)
    end
}

local t = setmetatable({ name = "outer" }, meta)
do
    local t = { name = "inner" }
    setmetatable(t, meta)
end

Plus de méthodes

Il y a beaucoup plus de méta-méthodes, certaines sont arithmétiques (par exemple addition, soustraction, multiplication), il y a des opérations binaires (et / ou xor, shift), des comparaisons (<,>) et des opérations de base comme == et # (égalité et longueur). Permet de construire une classe qui supporte plusieurs de ces opérations: un appel à l'arithmétique rationnelle. Bien que ce soit très simple, cela montre l’idée.

local meta = {
    -- string representation
    __tostring = function(self)
        return string.format("%s/%s", self.num, self.den)
    end,
    -- addition of two rationals
    __add = function(self, rhs)
        local num = self.num * rhs.den + rhs.num * self.den
        local den = self.den * rhs.den
        return new_rational(num, den)
    end,
    -- equality
    __eq = function(self, rhs)
        return self.num == rhs.num and self.den == rhs.den
    end
}

-- a function for the creation of new rationals
function new_rational(num, den)
    local self = { num = num, den = den }
    setmetatable(self, meta)

    return self
end

local r1 = new_rational(1, 2)
print(r1) -- 1/2

local r2 = new_rational(1, 3)
print(r1 + r2) -- 5/6

local r3 = new_rational(1, 2)
print(r1 == r3) -- true
-- this would be the behaviour if we hadn't implemented the __eq metamethod.
-- this compares the actual tables, which are different
print(rawequal(r1, r3)) -- false

Rendre les tables appelables

Il existe une méthode appelée __call , qui définit le bevahiour de l’objet lorsqu’il est utilisé comme une fonction, par exemple object() . Cela peut être utilisé pour créer des objets de fonction:

-- create the metatable with a __call metamethod
local meta = {
    __call = function(self)
        self.i = self.i + 1
    end,
    -- to view the results
    __tostring = function(self)
        return tostring(self.i)
    end
}

function new_counter(start)
    local self = { i = start }
    setmetatable(self, meta)
    return self
end

-- create a counter
local c = new_counter(1)
print(c) --> 1
-- call -> count up
c()
print(c) --> 2

La metamethod est appelée avec l'objet correspondant, tous les arguments restants sont transmis à la fonction après cela:

local meta = {
    __call = function(self, ...)
        print(self.prepend, ...)
    end
}

local self = { prepend = "printer:" }
setmetatable(self, meta)

self("foo", "bar", "baz")

Indexation des tables

L’utilisation la plus importante des métabalises est peut-être la possibilité de modifier l’indexation des tables. Pour cela, il y a deux actions à prendre en compte: lire le contenu et écrire le contenu de la table. Notez que les deux actions ne sont déclenchées que si la clé correspondante n'est pas présente dans la table.

En train de lire

local meta = {}

-- to change the reading action, we need to set the '__index' method
-- it gets called with the corresponding table and the used key
-- this means that table[key] translates into meta.__index(table, key)
meta.__index = function(object, index)
    -- print a warning and return a dummy object
    print(string.format("the key '%s' is not present in object '%s'", index, object))
    return -1
end

-- create a testobject
local t = {}

-- set the metatable
setmetatable(t, meta)

print(t["foo"]) -- read a non-existent key, prints the message and returns -1

Cela pourrait être utilisé pour générer une erreur en lisant une clé inexistante:

-- raise an error upon reading a non-existent key
meta.__index = function(object, index)
    error(string.format("the key '%s' is not present in object '%s'", index, object))
end

L'écriture

local meta = {}

-- to change the writing action, we need to set the '__newindex' method
-- it gets called with the corresponding table, the used key and the value
-- this means that table[key] = value translates into meta.__newindex(table, key, value)
meta.__newindex = function(object, index, value)
    print(string.format("writing the value '%s' to the object '%s' at the key '%s'",
                         value, object, index))
    --object[index] = value -- we can't do this, see below
end

-- create a testobject
local t = { }

-- set the metatable
setmetatable(t, meta)

-- write a key (this triggers the method)
t.foo = 42

Vous pouvez maintenant vous demander comment la valeur réelle est écrite dans le tableau. Dans ce cas, ce n'est pas le cas. Le problème ici est que les méthodes peuvent déclencher des méthodes, ce qui entraînerait une boucle infinitive, ou plus précisément un débordement de pile. Alors, comment pouvons-nous résoudre ce problème? La solution pour cela est appelée accès table brut .

Accès à la table brute

Parfois, vous ne voulez pas déclencher de métaméthode, mais vous écrivez ou lisez vraiment exactement la clé donnée, sans certaines fonctions astucieuses entourant l'accès. Pour cela, lua vous fournit des méthodes d'accès aux tables brutes:

-- first, set up a metatable that allows no read/write access
local meta = {
    __index = function(object, index)
        -- raise an error
        error(string.format("the key '%s' is not present in object '%s'", index, object))
    end,
    __newindex = function(object, index, value)
        -- raise an error, this prevents any write access to the table
        error(string.format("you are not allowed to write the object '%s'", object))
    end
}

local t = { foo = "bar" }
setmetatable(t, meta)

-- both lines raise an error:
--print(t[1])
--t[1] = 42

-- we can now circumvent this problem by using raw access:
print(rawget(t, 1)) -- prints nil
rawset(t, 1, 42) -- ok

-- since the key 1 is now valid, we can use it in a normal manner:
print(t[1])

Avec ceci, nous pouvons maintenant réécrire la méthode précédente de __newindex pour écrire réellement la valeur dans la table:

meta.__newindex = function(object, index, value)
    print(string.format("writing the value '%s' to the object '%s' at the key '%s'",
                         value, object, index))
    rawset(object, index, value)
end

Simulation de la POO

local Class = {} -- objects and classes will be tables
local __meta = {__index = Class}
-- ^ if an instance doesn't have a field, try indexing the class
function Class.new()
    -- return setmetatable({}, __meta) -- this is shorter and equivalent to:
    local new_instance = {}
    setmetatable(new_instance, __meta)
    return new_instance
end
function Class.print()
    print "I am an instance of 'class'"
end

local object = Class.new()
object.print() --> will print "I am an instance of 'class'"

Les méthodes d'instance peuvent être écrites en passant l'objet comme premier argument.

-- append to the above example
function Class.sayhello(self)
    print("hello, I am ", self)
end
object.sayhello(object) --> will print "hello, I am <table ID>"
object.sayhello() --> will print "hello, I am nil"

Il y a du sucre syntaxique pour cela.

function Class:saybye(phrase)
    print("I am " .. self .. "\n" .. phrase)
end
object:saybye("c ya") --> will print "I am <table ID>
                      -->             c ya"

Nous pouvons également ajouter des champs par défaut à une classe.

local Class = {health = 100}
local __meta = {__index = Class}

function Class.new() return setmetatable({}, __meta) end
local object = Class.new()
print(object.health) --> prints 100
Class.health = 50; print(object.health) --> prints 50
-- this should not be done, but it illustrates lua indexes "Class"
-- when "object" doesn't have a certain field
object.health = 200 -- This does NOT index Class
print(object.health) --> prints 200


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow