I. Problème▲
Avec des caractéristiques souples comme la surcharge et les templates type-safe, pourquoi un programmeur C++ devrait-il jamais écrire #define ?
II. Solution▲
Avec des caractéristiques souples comme la surcharge et les templates type-safe, pourquoi un programmeur C++ devrait-il jamais écrire #define ?
Les caractéristiques de C++ éliminent souvent, mais pas toujours, le besoin d'utiliser #define. Par exemple, la commande const int c = 42; est meilleure que #define c 42 parce qu'elle assure la sécurité du type, évite les éditions accidentelles préprocesseurs, et pour quelques autres raisons. Il existe néanmoins toujours quelques bonnes raisons d'écrire #define :
II-A. Header Guards▲
C'est une astuce habituelle pour éviter les inclusions multiples d'en-tête :
#ifndef MYPROG_X_H
#define MYPROG_X_H
// ... le reste du fichier d'en-tête x.h va ici...
#endif
II-B. Accès aux caractéristiques préprocesseurs▲
Souvent, on aime bien insérer des choses comme des numéros de ligne et des temps de construction en code diagnostic. Une façon facile de le faire consiste à utiliser des macro prédéfinies telles que __FILE__, __LINE__, __DATE__ et __TIME__. Pour la même raison et pour d'autres, il est souvent utile d'utiliser les opérateurs préprocesseurs de transformation en chaîne littérale et de concaténation de symboles (# et ##).
II-C. Code de sélection au moment de compilation (ou code spécifique du constructeur)▲
C'est la catégorie d'utilisation la plus riche et la plus importante pour le préprocesseur. Bien que je sois tout sauf un fan de la magie du préprocesseur, il y a des choses que vous ne pouvez pas faire aussi bien autrement, voire que vous ne pouvez pas faire du tout autrement.
II-C-1. Code de débogage▲
Parfois, vous voulez bâtir votre système avec certaines pièces de code "supplémentaires" (typiquement des informations de débogage) et parfois vous ne le voulez pas :
void
f()
{
#ifdef MY_DEBUG
cerr <<
"some trace logging"
<<
endl;
#endif
// ... le reste de f() va ici...
}
Il est possible de faire ça au moment où vous lancez le programme, bien sûr. En prenant la décision au moment de la compilation, vous évitez le surcoût à l'exécution, mais vous perdez également la souplesse de différer la décision jusqu'au moment de lancer le programme.
II-C-2. Code spécifique de plateforme▲
Habituellement, il est préférable de traiter le code spécifique à la plateforme dans une fabrique pour assurer une meilleure organisation du code et une plus grande souplesse pendant le fonctionnement du programme. Parfois, cependant, il existe trop peu de différences pour justifier une fabrique et le préprocesseur peut être une méthode pratique pour rendre du code facultatif. (1)
II-C-3. Variantes de représentation des données▲
Un exemple courant est qu'un module peut définir une liste de codes d'erreur, que des utilisateurs extérieurs devraient voir comme une simple énumération avec commentaires, mais qui, à l'intérieur du module, devrait être stockée dans une carte pour un affichage facile. Cela donne :
// Pour les utilisateurs externes :
enum
Errors {
ERR_OK =
0
, // No error
ERR_INVALID_PARAM =
1
// <description>
...
}
// Pour l'utilisation interne du module :
map<
Error,const
char
*>
lookup;
lookup.insert( make_pair( Error(0
), "No error"
) );
lookup.insert( make_pair( Error(1
), "<description>"
) );
...
Nous aimerions avoir les deux représentations sans
définir les informations concrètes (paires de code/message)
deux fois. Avec la magie des macros, on peut simplement
écrire une liste d'erreurs comme suit, en créant la
structure qui convient au moment de la compilation :
DERR_ENTRY( ERR_OK, 0
, "No error"
),
DERR_ENTRY( ERR_INVALID_PARAM, 1
, "<description>"
),
//...
Les implémentations de DERR_ENTRY et des macros qui y sont liées sont laissées au lecteur. Ce sont trois exemples courants ; il y en a beaucoup d'autres.
III. Remerciements▲
Cet article est une traduction par l'équipe de la rubrique C++ de l'article de Herb Sutter publié sur Guru of the Week. Vous pouvez retrouver cet article dans sa version originale sur le site de Guru of the Week : Preprocessor MacrosPreprocessor Macros.
Merci à Luc Hermitte et à Kalith pour leur relecture, à Gurdil le nain et à ClaudeLELOUP pour leur relecture orthographique.