- Inclusión de archivos fuente (#include)
- Visión general de las directivas de sustitución de macros
- Forma simple de #define
- Forma de #define como pseudofunción
- Operadores especiales '#' y '##' dentro de definiciones #define
- Anulación de la sustitución de macros (#undef)
- Constantes predefinidas del preprocesador
- Compilación condicional (#ifdef/#ifndef/#else/#endif)
- Propiedades generales del programa (#property)
Forma #define como pseudofunción
La sintaxis de la forma paramétrica #define es similar a la de una función.
#define macro_identifier(parameter,...) text_with_parameters |
Una macro de este tipo tiene uno o varios parámetros entre paréntesis. Los parámetros se separan con comas. Cada parámetro es un identificador simple (a menudo, una sola letra). Además, todos los parámetros de una macro deben tener identificadores diferentes.
Es importante que no haya ningún espacio entre el identificador y el paréntesis de apertura; de lo contrario, la macro se tratará como una forma simple en la que el texto de sustitución comienza con un paréntesis de apertura.
Una vez registrada esta directiva, el preprocesador buscará en los códigos fuente las líneas de forma:
macro_identifier(expression,...) |
Se pueden especificar expresiones arbitrarias en lugar de parámetros. El número de argumentos debe coincidir con el número de parámetros de la macro. Todos los casos que se encuentren se sustituirán por text_with_parameters, en los que, a su vez, los parámetros se sustituirán por las expresiones pasadas. Cada parámetro puede aparecer varias veces, en cualquier orden.
Por ejemplo, la siguiente macro encuentra el máximo de dos valores:
#define MAX(A,B) ((A) > (B) ? (A) : (B)) |
Si el código contiene la sentencia:
int z = MAX(x, y); |
el preprocesador la «expandirá» a:
int z = ((x) > (y) ? (x) : (y)); |
La sustitución de macros funcionará para cualquier tipo de datos (para los que sean válidas las operaciones aplicadas dentro de la macro).
Sin embargo, la sustitución también puede tener efectos secundarios. Por ejemplo, si el parámetro real es una llamada a una función o una sentencia que modifica la variable (digamos, ++x), entonces la acción correspondiente puede realizarse varias veces (en lugar de la única vez prevista). En el caso de MAX, esto ocurrirá dos veces: durante la comparación y al obtener valores en una de las ramas del operador '?:'. A este respecto, tiene sentido convertir estas macros en funciones siempre que sea posible (sobre todo teniendo en cuenta que en MQL5 las funciones se incorporan automáticamente).
Hay paréntesis alrededor de los parámetros y alrededor de toda la definición de la macro. Se utilizan para garantizar que la sustitución de expresiones como parámetros o de la propia macro dentro de otras expresiones no distorsione el orden de cálculo debido a prioridades diferentes. Supongamos que la macro define la multiplicación de dos parámetros (aún no encerrados entre paréntesis):
#define MUL(A,B) A * B |
Entonces, el uso de la macro con las siguientes expresiones producirá resultados inesperados:
int x = MUL(1 + 2, 3 + 4); // 1 + 2 * 3 + 4 |
En lugar de la multiplicación (1 + 2) * (3 + 4), que da 21, tenemos 1 + 2 * 3 + 4, es decir, 11. La definición de macro apropiada debería ser así:
#define MUL(A,B) ((A) * (B)) |
Puede especificar otra macro como parámetro de macro. Además, también puede insertar otras macros en una definición de macro. Todas estas macros se sustituirán secuencialmente. Por ejemplo:
#define SQ3(X) (X * X * X)
|
El siguiente código imprimirá 504 (MathAbs es una función integrada que devuelve el módulo de un número, es decir, sin signo):
int x = -10;
|
En la variable x, el valor -7 permanecerá (debido al triple incremento).
Una definición de macro puede contener paréntesis no coincidentes. Esta técnica se utiliza, por regla general, en un par de macros, uno de los cuales debe abrir un fragmento de código determinado y la otra debe cerrarlo. En este caso, los paréntesis no emparejados de cada uno de ellos se convertirán en emparejados. En concreto, en los archivos de la biblioteca estándar disponibles en el paquete de distribución de MetaTrader 5, en Controls/Defines.mqh, se definen las macros EVENT_MAP_BEGIN y EVENT_MAP_END, que se utilizan para formar la función de procesamiento de eventos en objetos gráficos.
El preprocesador lee línea por línea todo el texto fuente del programa, empezando por el archivo principal mq5 e insertando en su lugar los textos de los archivos de encabezado encontrados. En el momento en que se lee cualquier línea de código se forma un cierto conjunto de macros que ya están definidas. No importa en qué orden se definieron las macros: es muy posible que una macro haga referencia en su definición a otra, que se describió tanto arriba como abajo en el texto. Sólo es importante que en la línea de código fuente donde se utiliza el nombre de la macro se conozcan las definiciones de todas las macros referenciadas.
Veamos un ejemplo:
#define NEG(x) (-SQN(x))*TEN
|
En este caso, la macro NEG utiliza las macros SQN y TEN, que se describen a continuación, y esto no nos impide utilizarlo con éxito en el código después de los tres #define-s.
No obstante, si cambiamos la posición relativa de las filas por la siguiente:
#define NEG(x) (-SQN(x))*TEN
|
obtenemos un error de compilación de «identificador no declarado».