Directivas #if, #elif, #else y #endif (C/C++)

La directiva #if, con las directivas #elif, #else y #endif controla la compilación de partes de un archivo de código fuente. Si la expresión que se escribe (después de #if) tiene un valor distinto de cero, el grupo de líneas inmediatamente después de la directiva #if se conserva en la unidad de traducción.

Gramática

conditional :
if-part elif-partsopt else-partopt endif-line

if-part :
texto if-line

if-line :
#if constant-expression
identificador de #ifdef
identificador de #ifndef

elif-parts :
elif-line text
elif-parts elif-line text

elif-line :
#elif constant-expression

else-part :
texto de línea else

else-line :
#else

endif-line :
#endif

Comentarios

Cada directiva #if de un archivo de código fuente debe coincidir con una directiva #endif de cierre. Puede aparecer cualquier número de directivas #elif entre las directivas #if y #endif, pero como máximo se permite una directiva #else. La directiva #else, si está presente, debe ser la última directiva antes de #endif.

Las directivas #if, #elif, #else y #endif pueden anidar en las partes de texto de otras directivas #if. Cada directiva #else, #elif o #endif anidada pertenece a la directiva #if anterior más cercana.

Todas las directivas de compilación condicional, como #if y #ifdef, deben coincidir con una directiva #endif de cierre antes del final del archivo. De lo contrario, se genera un mensaje de error. Cuando las directivas de compilación condicional están contenidas en archivos de inclusión, deben cumplir las mismas condiciones: no debe haber directivas de compilación condicional sin coincidencia al final del archivo de inclusión.

El reemplazo de macros se realiza dentro de la parte de la línea que sigue a un comando #elif, por lo que se puede usar una llamada a una macro en constant-expression.

El preprocesador selecciona una de las apariciones dadas de text para procesamiento adicional. Un bloque especificado en text puede ser cualquier secuencia de texto. Puede ocupar más de una línea. Habitualmente, text suele ser texto de programa que tiene significado para el compilador o el preprocesador.

El preprocesador procesa el texto text seleccionado y lo pasa al compilador. Si text contiene directivas de preprocesador, el preprocesador ejecuta esas directivas. Solo se compilan los bloques de texto seleccionados por el preprocesador.

El preprocesador selecciona un solo elemento text mediante la evaluación de la expresión constante que sigue a cada directiva #if o #elif hasta que encuentra una expresión constante true (distinta de cero). Selecciona todo el texto (incluidas otras directivas de preprocesador que comiencen por #) hasta su #elif, #else, o #endif asociada.

Si todas las repeticiones de constant-expression son false o si no aparece ninguna directiva #elif, el preprocesador selecciona el bloque de texto después de la cláusula #else. Si no hay cláusula #else y todas las instancias de constant-expression en el bloque #if son false, no se selecciona ningún bloque de texto.

constant-expression es una expresión constante entera con estas restricciones adicionales:

  • Las expresiones deben tener tipo entero y solo pueden incluir constantes de tipo entero, constantes de caracteres y el operador defined.

  • La expresión no puede utilizar sizeof o un operador de conversión de tipo.

  • Es posible que el entorno de destino no pueda representar todos los intervalos de enteros.

  • La traducción representa el tipo int igual que el tipo long y unsigned int igual que unsigned long.

  • El traductor puede traducir constantes de caracteres a un conjunto de valores de código diferentes del conjunto para el entorno de destino. Para determinar las propiedades del entorno de destino, use una aplicación compilada para ese entorno para comprobar los valores de macro LIMITS. H.

  • La expresión no debe consultar el entorno y debe permanecer aislada de los detalles de implementación en el equipo de destino.

Operadores de preprocesador

definido

El operador de preprocesador defined se puede usar en expresiones constantes especiales, como muestra la siguiente sintaxis:

defined( identifier )
identificador definido

Esta expresión constante se considera true (distinta de cero) si identifier está definido actualmente. De lo contrario, la condición es false (0). Un identificador definido como texto vacío se considera definido. El operador defined se puede usar en una directiva #if y #elif, pero en nigún otro caso.

En el ejemplo siguiente, las directivas #if y #endif controlan la compilación de una de las tres llamadas de función:

#if defined(CREDIT)
    credit();
#elif defined(DEBIT)
    debit();
#else
    printerror();
#endif

La llamada de función a credit se compila si el identificador CREDIT está definido. Si el identificador DEBIT está definido, se compila la llamada de función a debit. Si no está definido ninguno de los identificadores, se compila la llamada a printerror. CREDIT y credit son identificadores distintos en C y C++ por la distinción entre mayúsculas y minúsculas de ambos.

Las instrucciones de compilación condicional del ejemplo siguiente suponen una constante simbólica previamente definida denominada DLEVEL.

#if DLEVEL > 5
    #define SIGNAL  1
    #if STACKUSE == 1
        #define STACK   200
    #else
        #define STACK   100
    #endif
#else
    #define SIGNAL  0
    #if STACKUSE == 1
        #define STACK   100
    #else
        #define STACK   50
    #endif
#endif
#if DLEVEL == 0
    #define STACK 0
#elif DLEVEL == 1
    #define STACK 100
#elif DLEVEL > 5
    display( debugptr );
#else
    #define STACK 200
#endif

El primer bloque #if muestra dos conjuntos de directivas anidadas #if, #elsey #endif. El primer conjunto de directivas se procesa solo si DLEVEL > 5 es true. Si no, se procesan las instrucciones después de #else.

Las directivas #elif y #else del segundo ejemplo se utilizan para elegir entre cuatro opciones, según el valor de DLEVEL. La constante STACK se establece en 0, 100 o 200, según de la definición de DLEVEL. Si DLEVEL es mayor que 5, la instrucción

#elif DLEVEL > 5
display(debugptr);

se compila y STACK no está definido.

Un uso común para la compilación condicional es evitar inclusiones múltiples del mismo archivo de encabezado. En C++, donde las clases suelen definirse en archivos de encabezado, se pueden utilizar construcciones como este para evitar varias definiciones:

/*  EXAMPLE.H - Example header file  */
#if !defined( EXAMPLE_H )
#define EXAMPLE_H

class Example
{
    //...
};

#endif // !defined( EXAMPLE_H )

El código anterior realiza comprobaciones para ver si se define la constante simbólica EXAMPLE_H. Si es así, el archivo se ha incluido y no necesita volver a procesarse. Si no, se define la constante EXAMPLE_H para marcar EXAMPLE.H como ya procesado.

__has_include

Visual Studio 2017, versión 15.3 y posteriores: determina si un encabezado de biblioteca está disponible para su inclusión:

#ifdef __has_include
#  if __has_include(<filesystem>)
#    include <filesystem>
#    define have_filesystem 1
#  elif __has_include(<experimental/filesystem>)
#    include <experimental/filesystem>
#    define have_filesystem 1
#    define experimental_filesystem
#  else
#    define have_filesystem 0
#  endif
#endif

Consulte también

Directivas de preprocesador