Directivas #if, #elif, #else, #endif
§1 Sinopsis
C++ ofrece la posibilidad de compilación condicional mediante la inclusión de ciertas directivas que controlan el comportamiento del preprocesador, de forma que este puede ignorar o compilar determinadas líneas del código en función de ciertas condiciones que son evaluadas durante el preproceso.
§2 Sintaxis
#if k-expresion-1
<seccion-1>
<#elif k-expresion-2
<seccion-2>
...
<#elif k-expresion-n
<seccion-n>
<#else
<seccion-final>
#endif
§3 Comentario
Las directivas condicionales #if, #elif, #else y #endif se comportan igual que las sentencias C/C++ de selección ( 4.10.2). Si k-expresion-1 -sujeta a posible macro-expansión- es cierta (distinto de cero), las líneas de código de seccion-1, que pueden estar vacías, ser líneas normales de código o incluso de preprocesado, son preprocesadas y pasadas al compilador. Si es falsa (cero), la seccion-1 es ignorada por completo.
En el caso de ser cierta, después que se ha procesado la seccion-1, el control pasa a la correspondiente #endif, con lo que se termina la sentencia condicional y se continúa con la sección siguiente. Si es falsa, el control pasa a la siguiente #elif, donde se evalúa k-expresion-2. Si es cierta, se procesa la seccion-2, tras lo cual el control pasa al correspondiente #endif. En caso contrario, si k-expresion-2 es falsa, el control pasa al siguiente #elif. Así sucesivamente hasta que se llega a algún #else o #endif (#else es opcional y se alcanza si todas las comprobaciones previas han sido falsas).
Las diferentes secciones pueden contener a su vez otras cláusulas condicionales anidadas en cualquier profundidad. Cada #if debe contar con su correspondiente #endif. Puede haber cualquier número de #elif, pero solo un #else (que debe ser el último).
§4 Las expresiones k-expresion-n deben evaluarse a una
constante entera. Es decir, debe ser una expresión constante que se reduzca a un
entero, aunque esta expresión puede tener ciertas restricciones (
3.2.3a).
Ejemplo
#if SYSTEM == SYSV
#define HDR "sysv.h"
#elif SYSTEM == BSD
#define HDR "bsd.h"
#elif SYSTEM == MSDOS
#define HDR "msdos.h"
#else
#define HDR "default.h"
#endif
#include HDR // incluir la cabecera adecuada.
En este caso, se supone que SYSV, BSD y MSDOS son constantes simbólicas que en algún punto están definidas como valores enteros. A su vez SYSTEM es igualmente una constante simbólica cuyo valor es un entero.
Nada impide que la expresión condicional sea a su vez una expresión compuesta, tal como se muestra en el siguiente ejemplo
tomado de un caso real de programación Windows.
#if defined(__WXGTK__) || defined(__WXMOTIF__) || defined(__WXMAC__) || defined(__WXMGL__) ||
defined(__WXX11__)
#include "mondrian.xpm"
#endif
En este caso los nombres en mayúsculas corresponden a constantes manifiestas ( 1.4.1a) definidas en distintos compiladores C++. Ver ejemplos adicionales en 4.9.10c y 1.4.
El ejemplo siguiente muestra unas sentencias de compilación condicional en función del valor de la constante simbólica
DLEVEL que se supone ha sido definida previamente.
#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 (primeras 15 líneas), contiene dos series de directivas #if, #else y #endif anidadas en él. La primera se procesa si es cierta la condición DLEVEL > 5. En caso contrario se procesa la serie situada después del #else.
El resto de líneas del ejemplo se utiliza para establecer el valor de la constante STACK a 0, 100 o 200 en función del valor de la constante DLEVEL, pero si su valor es mayor que 5, STACK queda indefinida, y en su lugar se compila una invocación a la función display utilizando debugptr como argumento.
§5 Operador defined
Tanto Borland C++ como MS VC++ disponen de este operador [1] que solo puede ser utilizado en conjunción con las directivas #if y #elif. Admite dos formas de sintaxis, una de ellas con aspecto de función:
#if defined ( <identificador> )
#if defined <identificador>
#elif defined ( <identificador> )
#elif defined <identificador>
§5.1 Descripción
Este operador devuelve cierto (distinto de cero) si identificador ha sido definido previamente mediante una directiva #define ( 4.9.10b). En caso contrario devuelve falso (0).
Como puede verse, cumple la misma función que la directiva #ifdef, de forma que las dos expresiones que siguen son equivalentes:
#if defined identificador
#ifdef identificador
Sin embargo, el operador tiene la ventaja de que permite construir expresiones más complejas en la misma directiva #if o #elif. Por ejemplo:
#if defined(identif1) && !defined(identifi2)
...
#elif defined(identif1) && defined(identifi2)
...
En el ejemplo siguiente, se utiliza el operador para controlar la compilación de una función entre tres posibilidades:
#if defined(CLIENTE)
cliente();
#elif defined(PROVEEDOR)
proveedor();
#else
printerror();
#endif
Si el identificador CLIENTE ha sido definido, se compila una llamada a la función cliente(). En caso contrario, si se ha definido el identificador PROVEEDOR, se compila una invocación a la función proveedor(); finalmente, en ausencia de alguna de las condiciones anteriores, se invoca la función printerror().
Es frecuente que este operador se utilice para formar directivas de guarda ( 4.9.10e)
[1] No incluído en el Estándard C++