Différences
Ci-dessous, les différences entre deux révisions de la page.
Les deux révisions précédentes Révision précédente Prochaine révision | Révision précédente | ||
2_programmation:macros:cette_commande_est_elle_definie [2018/06/03 15:15] joseph.wright |
2_programmation:macros:cette_commande_est_elle_definie [2021/09/27 23:41] (Version actuelle) bdumont Traduction et remaniement pour mettre les solutions modernes et fiables en premier |
||
---|---|---|---|
Ligne 1: | Ligne 1: | ||
- | --- | + | ====== Comment tester si une commande est définie? ====== |
- | title: Is this command defined? | + | |
- | category: programming | + | |
- | tags: macros | + | |
- | permalink: /FAQ-isdef | + | |
- | --- | + | |
- | Macro sets from the earliest days of TeX programming may be | + | ===== En TeX ===== |
- | observed to test whether commands exist by using | + | |
- | `\ifx\<command>\undefined` ‹_stuff_› … | + | |
- | (which of course actually tests that the command _doesn't_ | + | |
- | exist). LaTeX programmers can make use of the internal command | + | |
- | `\@ifundefined{cmd name}{action1}{action2}` | + | |
- | which executes `action1` if the command is undefined, and | + | |
- | `action2` if it is defined | + | |
- | (_cmd name_ is the command name only, omitting the `\` character). | + | |
- | The `\@ifundefined` command is based on the sequence | + | Le programme <latex>\TeX{}</latex> original, écrit par Donald Knuth, ne définit pas de commandes dédiées à cette tâche. Heureusement, [[1_generalites:glossaire:qu_est_ce_que_etex|ε-TeX]] définit deux primitives supplémentaires : |
- | ```latex | + | |
- | \expandafter \ifx \csname cmd name\endcsname \relax | + | |
- | ``` | + | |
- | which relies on the way `\csname` works: if the command doesn't | + | |
- | exist, it simply creates it as an alias for `\relax`. | + | |
- | So: what is wrong with these techniques? | + | * ''\ifdefined'' |
+ | * ''\ifcsname cmd name\endcsname'' | ||
- | Using `\undefined` blithely assumes that the command is indeed not | + | Les deux commandes utilisées dans l'exemple qui suit produisent le même effet : |
- | defined. This isn't entirely safe; one could make the name more | + | |
- | improbable, but that may simply make it more difficult to spot a | + | |
- | problem when things go wrong. LaTeX programmers who use the | + | |
- | technique will typically employ `\@undefined`, adding a single | + | |
- | level of obscurity. | + | |
- | The original `\@ifundefined` mechanism had the unfortunate property of | + | <code latex> |
- | polluting the name space: each test that turns out undefined adds a | + | |
- | name to the set TeX is holding, and often all those `\relax` | + | |
- | names serve no purpose whatever. | + | |
- | + | ||
- | David Kastrup offers the (rather tricky) | + | |
- | ```latex | + | |
- | {\expandafter}\expandafter\ifx \csname cmd name\endcsname\relax ... | + | |
- | ``` | + | |
- | which "creates" the `\relax`-command inside the group of the first | + | |
- | `\expandafter`, therefore forgets it again once the test is done. | + | |
- | The test is about as good as you can do with macros. | + | |
- | + | ||
- | The [ε-TeX system](FAQ-etex) system comes to our help here: it | + | |
- | defines two new primitives: | + | |
- | + | ||
- | + | ||
- | - `\ifdefined`, which tests whether a thing is defined (the | + | |
- | negative of comparing with `\undefined`, as it were), and | + | |
- | - `\ifcsname cmd name\endcsname`, which does the | + | |
- | negative of `\@ifundefined` without the `\relax`-command | + | |
- | side-effect. | + | |
- | + | ||
- | So, in an ε-TeX-based system, the following two conditional clauses do | + | |
- | the same thing: | + | |
- | ```latex | + | |
\ifdefined\foo | \ifdefined\foo | ||
\message{\string\foo\space is defined}% | \message{\string\foo\space is defined}% | ||
Ligne 69: | Ligne 22: | ||
\message{no command \string\foo}% | \message{no command \string\foo}% | ||
\fi | \fi | ||
- | ``` | + | </code> |
- | However, after using the original LaTeX | + | |
- | `\@ifundefined{foo}`…, the conditionals will detect the | + | However, after using the original LaTeX ''\@ifundefined{foo}...'', |
- | command as "existing" (since it has been `\let` to `\relax`); | + | the conditionals will detect the command as "existing" |
- | so it is important not to mix mechanisms for detecting the state of a | + | (since it has been ''\let'' to ''\relax'') ; so it is important |
- | command. | + | not to mix mechanisms for detecting the state of a command. |
+ | |||
+ | ===== En LaTeX ===== | ||
+ | |||
+ | Quand on programme en <latex>\LaTeX{}</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>\LaTeX{}</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>\LaTeX{}</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 <latex>\TeX{}</latex> 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>\LaTeX{}</latex>, ne fait que déplacer le problème. | ||
+ | |||
+ | La macro ''\@ifundefined'', elle, est définie dans le noyau de <latex>\LaTeX{}</latex>, ce qui permet d'éviter ce problème. Cependant, avant 2018, elle était définie de la manière suivante : | ||
+ | |||
+ | <code latex> | ||
+ | \expandafter \ifx \csname cmd name\endcsname \relax | ||
+ | </code> | ||
+ | |||
+ | 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 <latex>\TeX{}</latex> ; | ||
+ | * 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>\LaTeX{}</latex> pour être basée sur la primitive ε-TeX ''\ifdefined'', David Kastrup a proposé la solution suivante : | ||
+ | |||
+ | <code latex> | ||
+ | {\expandafter}\expandafter\ifx \csname cmd name\endcsname\relax ... | ||
+ | </code> | ||
+ | |||
+ | 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:// [[faquk>FAQ-isdef|Is this command defined?]] | ||
- | In the 2016 LaTeX release, the definition of `\@ifundefined` was adapted | + | {{htmlmetatags>metatag-keywords=(LaTeX,programmation LaTeX,tester si une macro est définie,savoir si une commande est définie) |
- | to use the ε-TeX `\ifcsname` and now tests for a command being undefined or `\relax` | + | metatag-og:title=(Is this command defined?) |
- | without the side effect of defining undefined commands to `\relax`. | + | metatag-og:site_name=(FAQ LaTeX francophone) |
+ | }} | ||