Comment tester si une commande est définie?

En TeX

Le programme \TeX{} 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 \LaTeX{}, 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 \LaTeX{} 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, \LaTeX{} 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 \TeX{} 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 \LaTeX{}, ne fait que déplacer le problème.

La macro \@ifundefined, elle, est définie dans le noyau de \LaTeX{}, 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 :

  • Chaque utilisation de \@ifundefined avec un nom de commande qui n'existe pas crée cette commande, définie comme identique à \relax ; si cette commande n'est pas redéfinie ensuite, elle est conservée inutilement en mémoire par le moteur \TeX{} ;
  • 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 \LaTeX{} 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?

2_programmation/macros/cette_commande_est_elle_definie.txt · Dernière modification: 2021/09/27 23:41 par bdumont
CC Attribution-Share Alike 4.0 International
Driven by DokuWiki Recent changes RSS feed Valid CSS Valid XHTML 1.0