Comment tester si une commande est définie?
En TeX
Le programme original, écrit par Donald Knuth, ne définit pas de commandes dédiées à cette tâche. Heureusement, ε-TeX définit deux primitives supplémentaires :
\ifdefined
\ifcsname cmd name\endcsname
Les deux commandes utilisées dans l'exemple qui suit produisent le même effet :
\ifdefined\foo \message{\string\foo\space is defined}% \else \message{no command \string\foo}% \fi % \ifcsname foo\endcsname \message{\string\foo\space is defined}% \else \message{no command \string\foo}% \fi
However, after using the original LaTeX \@ifundefined{foo}…
,
the conditionals will detect the command as “existing”
(since it has been \let
to \relax
) ; so it is important
not to mix mechanisms for detecting the state of a command.
En LaTeX
Quand on programme en , on peut directement utiliser
\@ifundefined{⟨cmd name⟩}{⟨action1⟩}{⟨action2⟩}
,
qui exécute ⟨action1⟩
si la commande n'est pas définie,
et ⟨action2⟩
dans le cas contraire
(⟨cmd name⟩
est le nom de la commande tout nu, sans son antislash \
).
Si vous utilisez une version de antérieure à 2018, il faut éviter de mélanger du code qui utilise les primitives d'ε-TeX avec du code qui utilise \@ifundefined
(voir ci-dessous pourquoi). Comme cela peut se produire d'une extension à l'autre, vous n'êtes jamais à l'abri d'une erreur…
Notez également que, même après 2018, va toujours renvoyer “vrai” si l'on utilise \@ifundefined
avec une commande définie comme un alias de \relax
.
Un peu d'histoire
On trouve dans d'anciennes macros écrites en le procédé suivant
pour tester l'existence d'une commande ⟨commande⟩
:
\ifx\⟨commande⟩\undefined⟨code à exécuter⟩
(Ceci exécute le code si la commande n'existe pas, bien sûr.)
Le fonctionnement de cette commande repose sur le principe que \undefined
n'est jamais défini (donc elle est égale à une autre commande non définie). Le problème est qu'il ne s'agit que d'une convention qui peut être ignorée par un autre auteur de macros : il y a donc toujours un risque que cette macro soit définie dans une extension chargée par l'utilisateur… Utiliser \@undefined
, comme on peut le voir dans certaines macros , ne fait que déplacer le problème.
La macro \@ifundefined
, elle, est définie dans le noyau de , ce qui permet d'éviter ce problème. Cependant, avant 2018, elle était définie de la manière suivante :
\expandafter \ifx \csname cmd name\endcsname \relax
Elle utilisait la propriété suivante de \csname
:
si la commande n'existe pas, elle est créée comme alias de \relax
.
Cette approche présente deux inconvénients :
- Si le même nom de commande est testé ensuite avec la primitive ε-TeX
\ifdefined
(par exemple dans le code d'une autre extension), le résultat sera un faux positif, car cette primitive considère aussi comme définie la commande\relax
et ses alias.
Avant que \@ifundefined
ne soit redéfinie dans le noyau pour être basée sur la primitive ε-TeX \ifdefined
, David Kastrup a proposé la solution suivante :
{\expandafter}\expandafter\ifx \csname cmd name\endcsname\relax ...
La commande testée est créée et définie comme \relax
à l'intérieur du groupe dans lequel est inclus le premier
\expandafter
: elle n'est donc pas conservée en mémoire après l'exécution de \@ifundefined
.
Source: Is this command defined?