Les macros sont des morceaux de texte copiés / collés que le pré-processeur mettra dans le code authentique; l'auteur de la macro espère que le remplacement produira un code valide.
Il y a trois bons "conseils" pour y parvenir:
Aidez la macro à se comporter comme un code authentique
Le code normal se termine généralement par un point-virgule. Si l'utilisateur affiche le code qui n'en a pas besoin ...
doSomething(1) ;
DO_SOMETHING_ELSE(2) // <== Hey? What's this?
doSomethingElseAgain(3) ;
Cela signifie que l'utilisateur s'attend à ce que le compilateur produise une erreur si le point-virgule est absent.
Mais la vraie vraie bonne raison est qu'à un moment donné, l'auteur de la macro devra peut-être remplacer la macro par une véritable fonction (peut-être en ligne). Donc, la macro devrait vraiment se comporter comme telle.
Nous devrions donc avoir une macro nécessitant un point-virgule.
Produire un code valide
Comme indiqué dans la réponse de jfm3, parfois la macro contient plus d'une instruction. Et si la macro est utilisée dans une instruction if, ce sera problématique:
if(bIsOk)
MY_MACRO(42) ;
Cette macro pourrait être étendue comme suit:
#define MY_MACRO(x) f(x) ; g(x)
if(bIsOk)
f(42) ; g(42) ; // was MY_MACRO(42) ;
La g
fonction sera exécutée quelle que soit la valeur debIsOk
.
Cela signifie que nous devons avoir à ajouter une portée à la macro:
#define MY_MACRO(x) { f(x) ; g(x) ; }
if(bIsOk)
{ f(42) ; g(42) ; } ; // was MY_MACRO(42) ;
Produire un code valide 2
Si la macro est quelque chose comme:
#define MY_MACRO(x) int i = x + 1 ; f(i) ;
Nous pourrions avoir un autre problème dans le code suivant:
void doSomething()
{
int i = 25 ;
MY_MACRO(32) ;
}
Parce qu'il s'élargirait comme:
void doSomething()
{
int i = 25 ;
int i = 32 + 1 ; f(i) ; ; // was MY_MACRO(32) ;
}
Ce code ne se compilera pas, bien sûr. Donc, encore une fois, la solution utilise une portée:
#define MY_MACRO(x) { int i = x + 1 ; f(i) ; }
void doSomething()
{
int i = 25 ;
{ int i = 32 + 1 ; f(i) ; } ; // was MY_MACRO(32) ;
}
Le code se comporte à nouveau correctement.
Combiner les effets point-virgule + scope?
Il existe un idiome C / C ++ qui produit cet effet: La boucle do / while:
do
{
// code
}
while(false) ;
Le do / while peut créer une portée, encapsulant ainsi le code de la macro, et a besoin d'un point-virgule à la fin, se développant ainsi dans le code qui en a besoin.
Le bonus?
Le compilateur C ++ optimisera la boucle do / while, car le fait que sa post-condition soit fausse est connu au moment de la compilation. Cela signifie qu'une macro comme:
#define MY_MACRO(x) \
do \
{ \
const int i = x + 1 ; \
f(i) ; g(i) ; \
} \
while(false)
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
MY_MACRO(42) ;
// Etc.
}
se développera correctement
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
do
{
const int i = 42 + 1 ; // was MY_MACRO(42) ;
f(i) ; g(i) ;
}
while(false) ;
// Etc.
}
et est ensuite compilé et optimisé comme
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
{
f(43) ; g(43) ;
}
// Etc.
}
void
type à la fin ... comme ((void) 0) .