Macro-définition

Un article de Wikipédia, l'encyclopédie libre.

En programmation informatique, une macro-définition ou simplement macro est l'association d'un texte de remplacement à un identificateur, tel que l'identificateur est remplacé par le texte dans tout usage ultérieur.

Le plus souvent, on permet également le passage de paramètres syntaxiques. L'usage d'une macro comme instruction est souvent appelée macro-instruction et l'opération de remplacement d'une macro-instruction par sa définition la macro-expansion. Les macros sont donc un moyen de faire de la métaprogrammation.

Macros en programmation[modifier | modifier le code]

Par substitutions de chaînes de caractères[modifier | modifier le code]

Une macro est un motif de substitution de texte pouvant prendre des arguments. Un langage de macro copie le texte donné en entrée sur sa sortie. Au cours du processus, le texte est décomposé en unités lexicales, et chaque fois qu'un nom de macro est rencontré, celui-ci est replacé par sa définition. Lorsque après une substitution, l'analyse reprend au début du texte inséré, le langage est dit récursif. La plupart des langages disposent également de structures conditionnelles.

L'idée d'utiliser ce mécanisme afin d'automatiser la génération de partie de code répétitives date de l'assembleur des années 1950. Cette idée a été formalisée en 1959[1] et 1960[2] en y introduisant les concepts de récursivité et de structure conditionnelle. Différentes implémentations de langage de macro ont été réalisées dans les années suivantes, GPM (1965)[3], M4 (1977)[4].

Définition d'une macro ADD avec trois paramètres A, B et C:

ADD, A, B, C ≡ FETCH, A
               ADD, B
               STORE, C

Texte en entrée:

ADD, X, Y, Z

Texte substitué:

FETCH, X
ADD, Y
STORE, Z


Premier exemple de macro donné en 1960 par Douglas McIlroy[2] correspondant à la séquence d'instruction d'une addition en assembleur.

Texte en entrée:

§DEF,ABC,<AB~1C~2AB>;
$ABC,XY,PQ;

Texte transformé:

ABXYCPQAB

La première ligne §DEF,ABC,<AB~1C~2AB>; est la définition d'une macro ABC avec comme texte de substitution AB~1C~2AB~1, ~2 désignent les paramètres positionnels.

La deuxième ligne $ABC,XY,PQ; correspond à l'appel de la macro avec deux paramètres.

Exemple de macro du langage GPM[3]

Les premiers langages faisaient une utilisation intensive de macros, celles-ci représentant une alternative à l'utilisation de fonctions ou de procédures lorsque le coût d'appel de la routine est important face ou coût de son traitement[5].

Langage C / C++[modifier | modifier le code]

Le préprocesseur du langage C utilise ainsi un langage de macro. La directive #define introduit une macro-définition qui permet de déclarer :

  • une constante ;
  • une macro-instruction ;
  • un type personnalisé.
Exemple de constante[modifier | modifier le code]

Une constante peut être déclarée par la directive #define NOMBRE_UTILISATEURS 25 + 3.

À chaque fois que le préprocesseur du compilateur rencontre l'étiquette NOMBRE_UTILISATEURS dans le code source, il la remplace par 25 + 3.

Afin d'éviter une mauvaise interprétation dans une situation telle que Groupes = NOMBRE_UTILISATEURS / 4 qui devient Groupes = 25 + 3 / 4 donne 25 et non 7 (puisque 3 / 4 = 0), il faut plutôt rédiger la déclaration comme #define NOMBRE_UTILISATEURS (25 + 3), ce qui donne Groupes = (25 + 3) / 4, ce qui donne bien 7.

Exemple de macro-instruction[modifier | modifier le code]

La valeur absolue peut être déclarée comme une macro-instruction : #define abs(x) ((x) < 0 ? - (x) : (x)). À chaque fois que le programme contiendra une construction de la forme abs(x)x est une expression quelconque, cette construction sera étendue comme ((x) < 0 ? - (x) : (x)).

Sur cet exemple, on observe l'un des dangers liés à des macro-instructions fondées sur des substitutions de chaînes de caractères : on ne vérifie absolument pas que x a un type arithmétique au moment de l'appel de la macro, et l'utilisateur ne se rendra compte d'éventuels problèmes que lors de compilation du code étendu, avec un message d'erreur faisant référence au code après expansion. Par ailleurs, si l'évaluation de x est coûteuse ou provoque des effets de bords, des problèmes se poseront puisque x sera évalué plusieurs fois.

Exemple de type personnalisé[modifier | modifier le code]

Un type personnalisé peut être déclarée par la directive #define ULONG unsigned long int.

À chaque fois que le préprocesseur du compilateur rencontre l'étiquette ULONG dans le code source, il la remplace par unsigned long int; ansi ULONG Test = 0; est remplacé par unsigned long int Test = 0;.

Systèmes de composition de texte[modifier | modifier le code]

Parallèlement aux langages de programmation, l'utilisation de macros est au cœur de certains systèmes de composition de texte. TeX est ainsi constitué de primitives complétées par de nombreuses macros, et LaTeX est entièrement constitué d'un jeux de macros au-dessus de TeX, destiné à en simplifier l'utilisation. Le système de documentation groff, utilisé dans les environnements Linux repose en grande partie sur l'utilisation de macros[6]. Les modèles utilisés par MediaWiki sont des macros.

.de P   \" DEfine paragraph macro
.ne 3   \" we NEed at least three lines
.sp     \" SPace down one line
.ti .5i \" Temporary Indent .5 inches
..      \" end the macro definition


Exemple de macro groff définissant un paragraphe[6]

Par transformation source à source[modifier | modifier le code]

Dans la famille Lisp[modifier | modifier le code]

Le langage Lisp permet de puissantes définitions de macros, avec manipulation de la syntaxe abstraite du code en paramètre par un programme Lisp. À la différence d'un appel de fonction qui retourne une valeur lors de l'exécution, un appel de macro est remplacé par l'arbre syntaxique abstrait retourné par la macro, souvent lors de la compilation (mais pas uniquement), ce qui permet de faire exécuter du code au compilateur : c'est de la métaprogrammation.

Les langages Common Lisp (CL), Scheme et Dylan ont un système de macros de ce type. Dans le cas de CL et Scheme, qui sont constitués de s-expressions, l'écriture des macros est naturelle car le code source manipulé est déjà sous la forme d'un arbre de syntaxe (c'est tout l'intérêt de cette représentation du code). Dans le cas de Dylan, la syntaxe concrète du langage, irrégulière à la façon de Haskell — dont elle s'inspire — complique la tâche du programmeur de macros à cause de l'écart entre l'apparence du code et sa structure syntaxique abstraite.

Primitives[modifier | modifier le code]

La primitive defmacro (dans CL) prend en entrée un ensemble de s-expressions non évaluées et renvoie en sortie une transformation syntaxique de ces expressions (une nouvelle s-expression). Il est possible de réaliser toutes sortes de vérifications statiques et de garantir la correction du code produit. Ce système est infiniment plus robuste que les substitutions de chaînes de caractères. Les macros de CL peuvent souffrir de la capture de variable accidentelle liée à l'utilisation dans une macro d'un symbole qui existe déjà dans l'environnement d'expansion de la macro.

Scheme a les primitives defmacro (identique à ce qui existe dans CL), syntax-rules et define-syntax qui sont plus complexes, plus difficiles à utiliser également mais qui présentent, selon ses défenseurs, l'avantage d'éviter systématiquement la capture non intentionnelle de variable : on l'appelle un système de macros hygiénique.

Types de macros[modifier | modifier le code]

Il existe différents types de macros en Lisp, nous examinons spécifiquement les macros de CL :

  • macros-fonctions (transformation de source à source) ;
  • macros de lecteur (en anglais : READER MACROS) qui permettent de modifier la table de lecture de la fonction READ (le parseur CL standard), par exemple pour lire et interpréter des syntaxes différentes des s-expressions ;
  • macros de compilateur (en anglais : COMPILER MACROS) qui permettent de spécifier le code d'une fonction pour des valeurs (ou domaines) particulières des paramètres.

Macros dans certains logiciels[modifier | modifier le code]

Certains logiciels, ou environnements, permettent d'associer des suites d'instructions complexes à des touches clavier; on parle alors de macros clavier.

Certains logiciels, tels que ceux contenus dans les suites bureautiques Microsoft Office, LibreOffice, Apache OpenOffice, StarOffice ou WordPerfect, contiennent des langages de programmation comme Visual Basic for Applications (VBA) ou Basic ou encore PerfectScript permettant de commander les fonctionnalités des logiciels. On appelle macros les programmes écrits au moyen de ces langages.

Notes et références[modifier | modifier le code]

  1. (en) Irwin Greenwald, « A Technique for Handling Macro Instructions », Communications of the ACM, vol. 2, no 11,‎ , p. 21--22 (DOI 10.1145/368481.368509)
  2. a et b (en) Douglas McIlroy, « Macro instruction extensions of compiler languages », Communications of the ACM, vol. 3, no 4,‎ , p. 214-220 (DOI 10.1145/367177.367223)
  3. a et b (en) Christopher Strachey, « A general purpose macrogenerator », The Computer Journal, vol. 8, no 3,‎ , p. 225–241 (DOI 10.1093/comjnl/8.3.225)
  4. (en) Brian Kernighan et Dennis Ritchie, The M4 Macro Processor, (lire en ligne)
  5. (en) A. J. Cole, Macro Processors, Cambridge University Press, , 2e éd. (ISBN 978-0-521-24259-2)
  6. a et b (en) Arnold Robbins, « What's GNU? groff », Linux Journal, vol. 1994, no 7,‎ (lire en ligne)