Preprocesador de C++: Cómo las Directivas Moldean tu Código
Los preprocesadores son las directivas, que dan instrucciones al compilador para preprocesar la información antes de que comience la compilación real.
Todas las directivas del preprocesador comienzan con #, y solo caracteres de espacio en blanco pueden aparecer antes de una directiva del preprocesador en una línea. Las directivas del preprocesador no son declaraciones de C++, por lo que no terminan en punto y coma (;).
Ya has visto una directiva #include en todos los ejemplos. Esta macro se usa para incluir un archivo de encabezado en el archivo fuente.
Hay un número de directivas del preprocesador soportadas por C++ como #include, #define, #if, #else, #line, etc. Veamos las directivas importantes:
El Preprocesador #define
La directiva del preprocesador #define crea constantes simbólicas. La constante simbólica se llama un macro y la forma general de la directiva es:
#define nombre-macro texto-reemplazo
Cuando esta línea aparece en un archivo, todas las ocurrencias subsecuentes del macro en ese archivo serán reemplazadas por texto-reemplazo antes de que el programa sea compilado. Por ejemplo:
#include <iostream>
using namespace std;
#define PI 3.14159
int main() {
cout << "Valor de PI: " << PI << endl;
return 0;
}
Ahora, hagamos el preprocesamiento de este código para ver el resultado asumiendo que tenemos el archivo de código fuente. Así que compilemos con la opción -E y redirijamos el resultado a test.p. Ahora, si revisas test.p, tendrá mucha información y al final, encontrarás el valor reemplazado como sigue:
$ gcc -E test.cpp > test.p
...
int main() {
cout << "Valor de PI: " << 3.14159 << endl;
return 0;
}
Macros Tipo Función
Puedes usar #define para definir un macro que tome argumentos de la siguiente manera:
#include <iostream>
using namespace std;
#define MIN(a,b) (((a)<(b)) ? a : b)
int main() {
int i, j;
i = 100;
j = 30;
cout << "El mínimo es " << MIN(i, j) << endl;
return 0;
}
Si compilamos y ejecutamos el código anterior, esto produciría el siguiente resultado:
El mínimo es 30
Compilación Condicional
Hay varias directivas, que pueden usarse para compilar porciones selectivas del código fuente de tu programa. Este proceso se llama compilación condicional.
La construcción condicional del preprocesador es muy parecida a la estructura de selección 'if'. Considera el siguiente código del preprocesador:
#ifndef NULL
#define NULL 0
#endif
Puedes compilar un programa para propósito de depuración. También puedes encender o apagar la depuración usando un solo macro de la siguiente manera:
#ifdef DEBUG
cerr << "Variable x = " << x << endl;
#endif
Esto causa que la declaración cerr sea compilada en el programa si la constante simbólica DEBUG ha sido definida antes de la directiva #ifdef DEBUG. Puedes usar la declaración #if 0 para comentar una porción del programa de la siguiente manera:
#if 0
código prevenido de compilar
#endif
Intentemos el siguiente ejemplo:
#include <iostream>
using namespace std;
#define DEBUG
#define MIN(a,b) (((a)<(b)) ? a : b)
int main() {
int i, j;
i = 100;
j = 30;
#ifdef DEBUG
cerr << "Traza: Dentro de función main" << endl;
#endif
#if 0
/* Esta parte está comentada */
cout << MKSTR(HOLA C++) << endl;
#endif
cout << "El mínimo es " << MIN(i, j) << endl;
#ifdef DEBUG
cerr << "Traza: Saliendo de función main" << endl;
#endif
return 0;
}
Si compilamos y ejecutamos el código anterior, esto produciría el siguiente resultado:
El mínimo es 30
Traza: Dentro de función main
Traza: Saliendo de función main
Los Operadores # y ##
Los operadores # y ## del preprocesador están disponibles en C++ y ANSI/ISO C. El operador # causa que un token de texto-reemplazo sea convertido a una cadena rodeada por comillas.
Considera la siguiente definición de macro:
#include <iostream>
using namespace std;
#define MKSTR(x) #x
int main() {
cout << MKSTR(HOLA C++) << endl;
return 0;
}
Si compilamos y ejecutamos el código anterior, esto produciría el siguiente resultado:
HOLA C++
Veamos cómo funcionó. Es simple de entender que el preprocesador de C++ convierte la línea:
cout << MKSTR(HOLA C++) << endl;
La línea anterior será convertida en la siguiente línea:
cout << "HOLA C++" << endl;
El operador ## se usa para concatenar dos tokens. Aquí hay un ejemplo:
#define CONCAT(x, y) x ## y
Cuando CONCAT aparece en el programa, sus argumentos son concatenados y usados para reemplazar el macro. Por ejemplo, CONCAT(HOLA, C++) es reemplazado por "HOLA C++" en el programa de la siguiente manera.
#include <iostream>
using namespace std;
#define concat(a, b) a ## b
int main() {
int xy = 100;
cout << concat(x, y);
return 0;
}
Si compilamos y ejecutamos el código anterior, esto produciría el siguiente resultado:
100
Veamos cómo funcionó. Es simple de entender que el preprocesador de C++ transforma:
cout << concat(x, y);
La línea anterior será transformada en la siguiente línea:
cout << xy;
Macros Predefinidos de C++
C++ proporciona un número de macros predefinidos mencionados a continuación:
| Nº | Macro y Descripción |
|---|---|
| 1 | __LINE__ Contiene el número de línea actual del programa cuando se está compilando. |
| 2 | __FILE__ Contiene el nombre de archivo actual del programa cuando se está compilando. |
| 3 | __DATE__ Contiene una cadena de la forma mes/día/año que es la fecha de la traducción del archivo fuente a código objeto. |
| 4 | __TIME__ Contiene una cadena de la forma hora:minuto:segundo que es la hora a la que el programa fue compilado. |
Veamos un ejemplo para todos los macros anteriores:
#include <iostream>
using namespace std;
int main() {
cout << "Valor de __LINE__: " << __LINE__ << endl;
cout << "Valor de __FILE__: " << __FILE__ << endl;
cout << "Valor de __DATE__: " << __DATE__ << endl;
cout << "Valor de __TIME__: " << __TIME__ << endl;
return 0;
}
Si compilamos y ejecutamos el código anterior, esto produciría el siguiente resultado:
Valor de __LINE__: 6
Valor de __FILE__: test.cpp
Valor de __DATE__: Feb 28 2011
Valor de __TIME__: 18:52:48
Lenguaje C
- Dominando las Directivas de Preprocesador en C#: Guía Práctica
- Entendiendo los Tipos de Datos en C++: Fundamentos, Modificadores y Mejores Prácticas
- Dominando los Operadores en C++: Guía Completa con Ejemplos Prácticos
- Comentarios en C++: Mejores Prácticas para Código Legible y Mantenible
- Entendiendo los Constructores en C++: Por Defecto, Parametrizados y de Copia Explicados
- Dominando las plantillas de clase en C++: guía práctica
- Entendiendo el Preprocesador de C (CPP): Cómo Simplifica la Compilación
- Lenguaje de Programación C++: Visión General y Características Clave
- Constantes y Literales en C++: Tipos, Sintaxis y Mejores Prácticas
- Guía Completa de Operadores en C++: Tipos, Uso y Mejores Prácticas
- Dominando los Tipos Numéricos en C++: Guía Práctica