Avec Lua, les variables globales n'ont pas besoin d'être déclarées.
Si les petits programmes s'accommodent très bien de cette facilité, il n'en est pas de même pour les gros programmes ou une simple faute de frappe peut provoquer d'énormes bugs.
Mais, vous pouvez changer un tel comportement, si vous le souhaitez.
Puisque Lua conserve ses variables globales dans une table classique, vous pouvez utiliser une metatable pour modifier son comportement lors de l'accès aux variables globales.
setmetatable(_G, { __newindex = function(_, n) error("tentative d'écrire dans une variable non déclarée, "..n, 2) end, __index = function(_, n) error("tentative de lire une variable non déclarée, "..n, 2) end, })
Après ce code, toute tentative d'accéder à une variable globale inexistante déclenchera une erreur.
Mais dans ces conditions, comment pouvez-vous déclarer de nouvelles variables?...
Tout simplement en utilisant la fonction rawset(), qui contourne la metamethode.
function Declare(name, initval) rawset( _G, name, initval or false ) end
La présence de or et de false assure que la nouvelle variable globale obtienne toujours une valeur différente de nil.
N'oubliez pas de définir cette fonction avant d'installer le contrôle d'accès, sinon, vous obtiendrez une erreur.
Cette fonction mise en place, vous permet d'obtenir un contrôle total sur les variables globales.
Si vous faites simplement: a = 1 vous obtiendrez le message suivant: "tentative d'écrire dans une variable non déclarée, a" Mais si vous faites: Declare"a", alors a sera égal à 1
Mais maintenant, pour tester si une variable existe, vous ne pouvez pas simplement la comparer à nil, car si elle est nil, son accès lèvera une erreur.
Alors à la place, vous allez utiliser rawget(), afin d'éviter la metamethod.
if rawget( _G, var) == nil then -- "var" n'est pas déclaré ... end
Il n'est pas difficile de changer cela afin de permettre le contrôle des variables globales de valeur égale à nil.
Vous avez simplement besoin d'une table auxiliaire qui maintienne les noms des variables déclarées.
Chaque fois qu'une métaméthode est appelée, elle vérifie dans ce tableau si la variable est ou non déclarée.
Le code pourrait ressembler à ce qui suit:
local declaredNames = {} function Declare(name, initval) rawset(_G, name, initval) declaredNames[name] = true end setmetatable( _G, { __newindex = function(t, n, v) if not declaredNames[n] then error("tentative d'écrire dans une variable non déclarée, "..n, 2) else rawset(t, n, v) end end, __index = function(_, n) if not declaredNames[n] then error("tentative de lire une variable non déclarée, "..n, 2) else return nil end end, })
Pour ces deux solutions, le surcoût est négligeable.
Avec la première solution, les métaméthodes ne sont jamais appelées en fonctionnement normal.
Dans la seconde, elles peuvent être appelées, mais seulement lorsque le programme accède à une variable de valeur égale à nil.