Una macro variádica es una característica de algunos lenguajes de programación informática , especialmente el preprocesador C , mediante la cual se puede declarar una macro para aceptar un número variable de argumentos .
Las macros con argumentos variables se introdujeron en 1999 en la revisión ISO/IEC 9899:1999 ( C99 ) del estándar del lenguaje C , y en 2011 en la revisión ISO/IEC 14882:2011 ( C++11 ) del estándar del lenguaje C++ . [1] La compatibilidad con macros variádicas sin argumentos se agregó en C++20 y se agregará en C23 . [2] [3]
La sintaxis de la declaración es similar a la de las funciones variádicas : se utiliza una secuencia de tres puntos " ... " para indicar que se deben pasar uno o más argumentos. Durante la expansión de la macro, cada aparición del identificador especial __VA_ARGS__ en la lista de reemplazo de la macro se reemplaza por los argumentos pasados.
Además, los argumentos de macro regulares pueden enumerarse antes de ...
, [4] pero no después de ...
.
No se proporciona ningún medio para acceder a los argumentos individuales en la lista de argumentos de la variable, ni para averiguar cuántos se pasaron. Sin embargo, se pueden escribir macros para contar la cantidad de argumentos que se han pasado. [5]
Tanto el estándar C99 como el C++11 requieren al menos un argumento, pero desde C++20 esta limitación se ha eliminado mediante la macro funcional __VA_OPT__ . La macro __VA_OPT__ se reemplaza por su argumento cuando hay argumentos presentes y se omite en caso contrario. Sin embargo, los compiladores comunes también permiten pasar cero argumentos antes de esta adición. [4] [6]
Las reglas del preprocesador de C impiden que los nombres de macro en el argumento de __VA_OPT__ se expandan recursivamente. Sin embargo, es posible evitar esta limitación hasta un número fijo arbitrario de expansiones recursivas. [7]
Varios compiladores admiten macros de argumentos variables al compilar código C y C++: GNU Compiler Collection 3.0, [4] Clang (todas las versiones), [8] Visual Studio 2005 , [6] C++Builder 2006 y Oracle Solaris Studio (anteriormente Sun Studio) Forte Developer 6 update 2 (C++ versión 5.3). [9] GCC también admite dichas macros al compilar Objective-C .
Se agregó soporte para la macro __VA_OPT__ para admitir cero argumentos en GNU Compiler Collection 8, [10] Clang 6, [11] y Visual Studio 2019. [ 12]
Si se desea una funciónprintf
similar , que tome como argumentos el archivo y el número de línea desde donde fue llamada, se aplica la siguiente solución. dbgprintf()
void realdbgprintf ( const char * NombreArchivoOrigen , int NombreLíneaOrigen , const char * CadenaFormatC , ...);
Debido a las limitaciones del soporte de macros variádicas en C++11, la siguiente solución sencilla puede fallar y, por lo tanto, debe evitarse:
#define dbgprintf(cformat, ...) \ realdbgprintf(__ARCHIVO__, __LÍNEA__, cformat, __VA_ARGS__)
La razón es que
dbgprintf ( "Hola" )
se expande a
realdbgprintf ( __ARCHIVO__ , __LÍNEA__ , "Hola" , )
#define dbgprintf(cformat, ...) \ realdbgprintf(__ARCHIVO__, __LÍNEA__, cformat, ##__VA_ARGS__)
#define dbgprintf(cformat, ...) \ realdbgprintf (__ARCHIVO__, __LÍNEA__, cformat __VA_OPT__(,) __VA_ARGS__)
Evitar las incompatibilidades mencionadas anteriormente. Es una tarea complicada, pero portátil.
#define dbgprintf(...) realdbgprintf (__ARCHIVO__, __LÍNEA__, __VA_ARGS__)
dbgprintf()
Podría entonces llamarse como
dbgprintf ( "Hola, mundo" );
que se expande a
realdbgprintf ( __FILE__ , __LINE__ , "Hola, mundo" );
Otro ejemplo es
dbgprintf ( "%d + %d = %d" , 2 , 2 , 5 );
que se expande a
realdbgprintf ( __ARCHIVO__ , __LÍNEA__ , "%d + %d = %d" , 2 , 2 , 5 );
Sin macros variádicas, no es posible escribir contenedores printf
directamente. La solución alternativa estándar es usar la funcionalidad stdargs de C/C++ y hacer que se llame a la función vprintf
en su lugar.
Existe un problema de portabilidad con la generación de una coma final con argumentos vacíos para macros variádicas en C99 . Algunos compiladores (por ejemplo, Visual Studio cuando no utiliza el nuevo preprocesador conforme al estándar [6] ) eliminarán silenciosamente la coma final. Otros compiladores (por ejemplo: GCC [4] ) admiten la colocación ##
antes de __VA_ARGS__
.
# define MYLOG(FormatLiteral, ...) fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, __VA_ARGS__)
La siguiente aplicación funciona
MYLOG ( "Demasiados globos %u" , 42 );
que se expande a
fprintf ( stderr , "%s(%u): " "Demasiados globos %u" " \n " , __FILE__ , __LINE__ , 42 );
que es equivalente a
fprintf ( stderr , "%s(%u): Demasiados globos %u \n " , __FILE__ , __LINE__ , 42 );
Pero mira esta aplicación:
MYLOG ( "¡Atención!" );
que se expande a
fprintf ( stderr , "%s(%u): " "¡Atención!" " \n " , __FILE__ , __LINE__ , );
lo que genera un error de sintaxis con GCC.
GCC admite la siguiente extensión (no portátil):
# define MYLOG(FormatLiteral, ...) fprintf(stderr, "%s(%u): " FormatLiteral "\n", __FILE__, __LINE__, ##__VA_ARGS__)
que elimina la coma final cuando __VA_ARGS__
está vacío.
C23 resuelve este problema introduciendo __VA_OPT__
algo similar a C++. [3]
Antes de la existencia de argumentos variables en C99, era bastante común utilizar paréntesis doblemente anidados para explotar la cantidad variable de argumentos que se podían suministrar a la printf()
función:
#define dbgprintf(x) realdbgprintf x
dbgprintf()
Podría entonces llamarse así:
dbgprintf (( "Hola, mundo %d" , 27 ));
que se expande a:
realdbgprintf ( "Hola, mundo %d" , 27 );