Las macros son piezas de texto copiadas / pegadas que el preprocesador colocará en el código original; El autor de la macro espera que el reemplazo produzca un código válido.
Hay tres buenos "consejos" para tener éxito en eso:
Ayuda a que la macro se comporte como un código genuino
El código normal generalmente termina con un punto y coma. Si el usuario ve el código que no necesita uno ...
doSomething(1) ;
DO_SOMETHING_ELSE(2) // <== Hey? What's this?
doSomethingElseAgain(3) ;
Esto significa que el usuario espera que el compilador produzca un error si el punto y coma está ausente.
Pero la verdadera buena razón es que, en algún momento, el autor de la macro tal vez necesite reemplazar la macro con una función genuina (tal vez en línea). Entonces la macro realmente debería comportarse como tal.
Entonces deberíamos tener una macro que necesite punto y coma.
Producir un código válido
Como se muestra en la respuesta de jfm3, a veces la macro contiene más de una instrucción. Y si la macro se usa dentro de una declaración if, esto será problemático:
if(bIsOk)
MY_MACRO(42) ;
Esta macro podría expandirse como:
#define MY_MACRO(x) f(x) ; g(x)
if(bIsOk)
f(42) ; g(42) ; // was MY_MACRO(42) ;
La g
función se ejecutará independientemente del valor de bIsOk
.
Esto significa que debemos agregar un ámbito a la macro:
#define MY_MACRO(x) { f(x) ; g(x) ; }
if(bIsOk)
{ f(42) ; g(42) ; } ; // was MY_MACRO(42) ;
Producir un código válido 2
Si la macro es algo como:
#define MY_MACRO(x) int i = x + 1 ; f(i) ;
Podríamos tener otro problema en el siguiente código:
void doSomething()
{
int i = 25 ;
MY_MACRO(32) ;
}
Porque se expandiría como:
void doSomething()
{
int i = 25 ;
int i = 32 + 1 ; f(i) ; ; // was MY_MACRO(32) ;
}
Este código no se compilará, por supuesto. Entonces, nuevamente, la solución está usando un alcance:
#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) ;
}
El código se comporta correctamente nuevamente.
¿Combina los efectos de punto y coma + alcance?
Hay un modismo C / C ++ que produce este efecto: el bucle do / while:
do
{
// code
}
while(false) ;
El do / while puede crear un ámbito, encapsulando así el código de la macro, y necesita un punto y coma al final, expandiéndose así en el código que lo necesita.
El bono?
El compilador de C ++ optimizará el ciclo do / while, ya que el hecho de que su condición posterior sea falsa se conoce en el momento de la compilación. Esto significa que una macro como:
#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 expandirá correctamente como
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.
}
y luego se compila y optimiza como
void doSomething(bool bIsOk)
{
int i = 25 ;
if(bIsOk)
{
f(43) ; g(43) ;
}
// Etc.
}
void
tipo al final ... like ((void) 0) .