#ifdef vs #if - ¿cuál es mejor / más seguro como método para habilitar / deshabilitar la compilación de secciones particulares de código?

StackOverflow https://stackoverflow.com/questions/135069

Pregunta

Esto puede ser una cuestión de estilo, pero hay un poco de división en nuestro equipo de desarrollo y me pregunté si alguien más tenía alguna idea al respecto ...

Básicamente, tenemos algunas declaraciones de impresión de depuración que desactivamos durante el desarrollo normal. Personalmente prefiero hacer lo siguiente:

//---- SomeSourceFile.cpp ----

#define DEBUG_ENABLED (0)

...

SomeFunction()
{
    int someVariable = 5;

#if(DEBUG_ENABLED)
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

Aunque algunos miembros del equipo prefieren lo siguiente:

// #define DEBUG_ENABLED

...

SomeFunction()
{
    int someVariable = 5;

#ifdef DEBUG_ENABLED
    printf("Debugging: someVariable == %d", someVariable);
#endif
}

... ¿Cuál de esos métodos te suena mejor y por qué? Mi sensación es que la primera es más segura porque siempre hay algo definido y no hay peligro de que pueda destruir otras definiciones en otro lugar.

¿Fue útil?

Solución

Mi reacción inicial fue #ifdef , por supuesto , pero creo que #if en realidad tiene algunas ventajas significativas para esto. He aquí por qué:

Primero, puede usar DEBUG_ENABLED en el preprocesador y de las pruebas compiladas. Ejemplo: a menudo, quiero tiempos de espera más largos cuando la depuración está habilitada, por lo que al usar #if , puedo escribir esto

  DoSomethingSlowWithTimeout(DEBUG_ENABLED? 5000 : 1000);

... en lugar de ...

#ifdef DEBUG_MODE
  DoSomethingSlowWithTimeout(5000);
#else
  DoSomethingSlowWithTimeout(1000);
#endif

Segundo, estás en una mejor posición si quieres migrar de una #define a una constante global. La mayoría de los programadores de C ++ desaprueban los #define .

Y, tercero, dices que tienes una división en tu equipo. Supongo que esto significa que diferentes miembros ya han adoptado diferentes enfoques, y usted necesita estandarizar. Decidir que #if es la opción preferida significa que el código que usa #ifdef se compilará y ejecutará, incluso cuando DEBUG_ENABLED sea falso. Y es mucho más fácil de rastrear y eliminar la salida de depuración que se produce cuando no debería ser, y viceversa.

Ah, y un punto de legibilidad menor. Debería poder usar verdadero / falso en lugar de 0/1 en su #define , y debido a que el valor es un solo token léxico, es la única vez que no necesita paréntesis a su alrededor.

#define DEBUG_ENABLED true

en lugar de

#define DEBUG_ENABLED (1)

Otros consejos

Ambos son horribles. En su lugar, haz esto:

#ifdef DEBUG
#define D(x) do { x } while(0)
#else
#define D(x) do { } while(0)
#endif

Luego, cuando necesite un código de depuración, póngalo dentro de D (); . Y su programa no está contaminado con laberintos horribles de #ifdef .

#ifdef simplemente verifica si se ha definido un token, dado

#define FOO 0

entonces

#ifdef FOO // is true
#if FOO // is false, because it evaluates to "#if 0"

Hemos tenido este mismo problema en varios archivos y siempre existe el problema de que las personas se olviden de incluir una " marca de características " archivo (con una base de código de > 41,000 archivos es fácil de hacer).

Si tuvieras feature.h:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE 1

#endif // FEATURE_H

Pero luego se olvidó de incluir el archivo de encabezado en file.cpp:

#if COOL_FEATURE
    // definitely awesome stuff here...
#endif

Luego tienes un problema, el compilador interpreta que COOL_FEATURE no está definido como " false " En este caso ya no se incluye el código. Sí, gcc admite un indicador que provoca un error para macros no definidas ... pero la mayoría de los códigos de terceros definen o no definen características, por lo que no sería tan portátil.

Hemos adoptado una forma portátil de corregir este caso, así como probar el estado de una función: las macros de funciones.

si ha cambiado la característica anterior.h a:

#ifndef FEATURE_H
#define FEATURE_H

// turn on cool new feature
#define COOL_FEATURE() 1

#endif // FEATURE_H

Pero luego nuevamente olvidaste incluir el archivo de encabezado en archivo.cpp:

#if COOL_FEATURE()
    // definitely awseome stuff here...
#endif

El preprocesador habría fallado debido al uso de una macro de función no definida.

A los efectos de realizar la compilación condicional, #if y #ifdef son casi iguales, pero no del todo. Si su compilación condicional depende de dos símbolos, entonces #ifdef no funcionará también. Por ejemplo, suponga que tiene dos símbolos de compilación condicional, PRO_VERSION y TRIAL_VERSION, podría tener algo como esto:

#if defined(PRO_VERSION) && !defined(TRIAL_VERSION)
...
#else
...
#endif

Usar #ifdef lo anterior se vuelve mucho más complicado, especialmente para que la parte #else funcione.

Trabajo en código que usa la compilación condicional de manera extensa y tenemos una mezcla de #if & amp; #ifdef. Tendemos a usar # ifdef / # ifndef para el caso simple y # si siempre se están evaluando dos o más símbolos.

Creo que es completamente una cuestión de estilo. Ninguno tiene realmente una ventaja obvia sobre el otro.

La consistencia es más importante que cualquiera de las dos opciones en particular, por lo que te recomiendo que te reúnas con tu equipo, elijas un estilo y te mantengas en él.

Yo mismo prefiero:

#if defined(DEBUG_ENABLED)

Dado que facilita la creación de código que busca la condición opuesta, es mucho más fácil de detectar: ??

#if !defined(DEBUG_ENABLED)

vs.

#ifndef(DEBUG_ENABLED)

Es una cuestión de estilo. Pero recomiendo una forma más concisa de hacer esto:

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print
#endif

debug_print("i=%d\n", i);

Haces esto una vez, luego siempre usas debug_print () para imprimir o no hacer nada. (Sí, esto se compilará en ambos casos). De esta manera, su código no será confuso con las directivas de preprocesador.

Si aparece la advertencia " la expresión no tiene efecto " y desea deshacerse de él, aquí hay una alternativa:

void dummy(const char*, ...)
{}

#ifdef USE_DEBUG
#define debug_print printf
#else
#define debug_print dummy
#endif

debug_print("i=%d\n", i);

#if le da la opción de configurarlo en 0 para desactivar la funcionalidad, mientras sigue detectando que el interruptor está allí.
Personalmente siempre #define DEBUG 1 para poder atraparlo con un #if o #ifdef

#if y #define MY_MACRO (0)

Usar #if significa que creaste un " define " macro, es decir, algo que se buscará en el código que se reemplazará por " (0) " ;. Este es el " macro hell " Odio ver en C ++, porque contamina el código con posibles modificaciones de código.

Por ejemplo:

#define MY_MACRO (0)

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

da el siguiente error en g ++:

main.cpp|408|error: lvalue required as left operand of assignment|
||=== Build finished: 1 errors, 0 warnings ===|

Sólo un error .

Lo que significa que su macro interactuó con éxito con su código C ++: la llamada a la función fue exitosa. En este sencillo caso, es divertido. Pero mi propia experiencia con las macros que juegan en silencio con mi código no está llena de alegría ni de completo, así que ...

#ifdef y #define MY_MACRO

Usar #ifdef significa que " define " alguna cosa. No es que le des un valor. Todavía está contaminando, pero al menos, será reemplazado por nada, y no será visto por el código C ++ como una declaración de código lagítimo. El mismo código de arriba, con una simple definición, es:

#define MY_MACRO

int doSomething(int p_iValue)
{
   return p_iValue + 1 ;
}

int main(int argc, char **argv)
{
   int MY_MACRO = 25 ;
   doSomething(MY_MACRO) ;

   return 0;
}

Da las siguientes advertencias:

main.cpp||In function ‘int main(int, char**)’:|
main.cpp|406|error: expected unqualified-id before ‘=’ token|
main.cpp|399|error: too few arguments to function ‘int doSomething(int)’|
main.cpp|407|error: at this point in file|
||=== Build finished: 3 errors, 0 warnings ===|

Entonces ...

Conclusión

Prefiero vivir sin macros en mi código, pero por varias razones (definición de protectores de encabezado o depuración de macros), no puedo.

Pero al menos, me gusta que sean lo menos interactivos posible con mi código legítimo de C ++. Lo que significa usar #define sin valor, usar #ifdef y #ifndef (o incluso #if definido como lo sugiere Jim Buck), y sobre todo, darles nombres tan largos y tan ajenos que nadie en su sano juicio usará es " por casualidad " ;, y de ninguna manera afectará al código legítimo de C ++.

Publicar Scriptum

Ahora, mientras vuelvo a leer mi publicación, me pregunto si no debería intentar encontrar algún valor que nunca sea correcto para agregar a C ++. Algo como

#define MY_MACRO @@@@@@@@@@@@@@@@@@

que podría usarse con #ifdef y #ifndef, pero no permitir que el código se compile si se usa dentro de una función ... Probé esto con éxito en g ++, y le di el error:

main.cpp|410|error: stray ‘@’ in program|

Interesante. :-)

El primero me parece más claro. Parece más natural que sea una bandera en comparación con definido / no definido.

Ambos son exactamente equivalentes. En el uso idiomático, #ifdef se usa solo para verificar la definición (y lo que yo usaría en su ejemplo), mientras que #if se usa en expresiones más complejas, como #if defined (A) & amp; & amp; ! definido (B).

Un poco de OT, pero activar / desactivar el registro con el preprocesador es definitivamente subóptimo en C ++. Hay buenas herramientas de registro como log4cxx de Apache, que son de código abierto y no restringen Cómo distribuyes tu aplicación. También le permiten cambiar los niveles de registro sin recompilación, tienen gastos generales muy bajos si desactiva el registro y le da la oportunidad de desactivarlo completamente en producción.

Eso no es una cuestión de estilo en absoluto. También la pregunta es lamentablemente incorrecta. No puede comparar estas directivas de preprocesador en el sentido de mejor o más seguro.

#ifdef macro

significa " si la macro está definida " o " si la macro existe " ;. El valor de la macro no importa aquí. Puede ser lo que sea.

#if macro

si siempre se compara con un valor. En el ejemplo anterior, es la comparación implícita estándar:

#if macro !=0

ejemplo para el uso de #if

#if CFLAG_EDITION == 0
    return EDITION_FREE;
#elif CFLAG_EDITION == 1
    return EDITION_BASIC;
#else
    return EDITION_PRO;
#endif

ahora puedes poner la definición de CFLAG_EDITION en tu código

#define CFLAG_EDITION 1 

o puede establecer la macro como indicador de compilador. También vea aquí .

Solía ??usar #ifdef , pero cuando cambié a Doxygen para la documentación, descubrí que las macros comentadas no se pueden documentar (o, al menos, Doxygen produce una advertencia). Esto significa que no puedo documentar las macros de cambio de función que no están habilitadas actualmente.

Aunque es posible definir las macros solo para Doxygen, esto significa que las macros en las partes no activas del código también serán documentadas. Personalmente quiero mostrar los cambios de funciones y, de lo contrario, solo documentar lo que está seleccionado actualmente. Además, hace que el código sea bastante desordenado si hay muchas macros que deben definirse solo cuando Doxygen procesa el archivo.

Por lo tanto, en este caso, es mejor definir siempre las macros y usar #if .

Siempre he usado #ifdef y banderas del compilador para definirlo ...

Alternativamente, puede declarar una constante global y usar C ++ si, en lugar del preprocesador #if. El compilador debe optimizar las ramas que no se utilicen para usted, y su código estará más limpio.

Esto es lo que C ++ Gotchas de Stephen C. Dewhurst dice acerca del uso de # if's.

Hay una diferencia en el caso de una manera diferente de especificar una definición condicional para el controlador:

diff <( echo | g++ -DA= -dM -E - ) <( echo | g++ -DA -dM -E - )

salida:

344c344
< #define A 
---
> #define A 1

Esto significa que -DA es sinónimo de -DA = 1 y, si se omite el valor, puede ocasionar problemas en el caso de #if Un uso .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top