Macros avec paramètres (C++)

De RAD Studio
Aller à : navigation, rechercher

Remonter à Le préprocesseur - Index


La syntaxe suivante est utilisée pour définir une macro avec des paramètres :

#define macro_identifier(<arg_list>) <token_sequence>

Aucun espace ne peut séparer macro_identifier et la parenthèse ouvrante (. L'argument facultatif arg_list est une séquence d'identificateurs séparés par des virgules, comme la liste d'arguments d'une fonction C. Néanmoins, toute virgule située entre des parenthèses internes correspondantes d'une liste d'arguments est traitée en tant que partie de l'argument, et non comme un délimiteur d'argument. Chaque identificateur délimité par des virgules joue le rôle d'un argument formel ou marque de réservation.

De telles macros sont appelées en écrivant

macro_identifier<whitespace>(<actual_arg_list>)

dans le code source qui suit. La syntaxe est identique à celle d'un appel de fonction. Néanmoins, il existe des différences sémantiques importantes et certains pièges.

La liste actual_arg_list facultative doit contenir le même nombre de séquences de tokens délimitées par des virgules, appelés arguments réels, que dans la liste arg_list formelle de la ligne #define. Il doit y avoir un argument réel pour chaque argument formel. Une erreur est signalée si les deux listes ne comportent pas le même nombre d'arguments.

Un appel de macro a pour résultat deux ensembles de remplacements. Tout d'abord, macro_identifier et les arguments entre parenthèses sont remplacés par la séquence token_sequence. Ensuite, les arguments formels rencontrés dans la séquence token_sequence sont remplacés par les arguments réels correspondants apparaissant dans la liste actual_arg_list.

Comme avec les définitions de macros simples, une deuxième analyse est effectuée pour détecter tous les identificateurs de macros imbriqués, admissibles pour l'expansion.

Parenthèses imbriquées et virgules

La liste actual_arg_list peut contenir des parenthèses imbriquées, à condition qu'elles aillent par paire. De même, les virgules situées entre les parenthèses ne sont pas traitées comme des délimiteurs d'arguments.

#define ERRMSG(x, str) printf("Error: %d \n%s", x, str)
#define SUM(x,y)  ((x) + (y))
:
ERRMSG(2, "Press Enter, then ESC");
/*expands to:  printf("Error: %d \n%s", 2, "Press Enter, then ESC"); */
return SUM(f(i, j), g(k, l));
/*expands to:  return ( (f(i, j)) + (g(k, l)) ); */

Effets de bord et autres risques

Les similitudes entre les appels de fonction et les appels de macro masquent souvent leur différence. Un appel de macro ne prévoit aucune vérification intégrée de types, de sorte qu'une non concordance entre les types de données des arguments réels et formels risque de donner des résultats étranges, difficiles à déboguer, et sans aucun avertissement. Les appels de macros peuvent aussi avoir des effets secondaires indésirables, notamment quand un argument réel est évalué plusieurs fois.

Comparez CUBE et cube dans l'exemple suivant :

int cube(int x) {
   return x*x*x;
  }
#define CUBE(x)  ( (x) * (x) * (x) )
...
int b = 0, a = 3;
b = cube(a++);
/* cube() is passed actual argument a = 3; so b = 27; now a = 4 */
a = 3;
b = CUBE(a++);
/* expands as ( (a++) * (a++) * (a++) ); again b = 27 but now a = 6 */

Conversion des arguments réels en chaînes avec l'opérateur #

Vous pouvez placer le symbole # devant un argument formel d'une macro pour convertir l'argument réel en chaîne après le remplacement.

Ainsi, avec la définition suivante :

#define TRACE(flag) printf(#flag "=%d\n", flag)

le fragment de code

int highval = 1024;
TRACE(highval);

devient

int highval = 1024;
printf("highval" "=%d\n", highval);

qui à son tour est traité comme

int highval = 1024;
printf("highval=%d\n", highval);


Voir aussi