Articulo de referencia

macro variádica

Una macro variádica es una característica de algunos lenguajes de programación informática , especialmente del preprocesador C , mediante la cual se puede declarar una macro par...

Una macro variádica es una característica de algunos lenguajes de programación informática , especialmente del preprocesador C , mediante la cual se puede declarar una macro para que acepte 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 ] El soporte para macros variádicas sin argumentos se agregó en C++20 y C23 . [ 2 ] [ 3 ]

C/C++

Sintaxis de declaración

La sintaxis de 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 macros se reemplaza por los argumentos pasados.

Además, los argumentos macro regulares pueden aparecer antes del ..., [ 4 ] pero los argumentos regulares no pueden aparecer después del ....

No se proporciona ningún medio para acceder a los argumentos individuales en la lista de argumentos variables, ni para averiguar cuántos se pasaron. Sin embargo, se pueden escribir macros para contar el número 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 y C23 esta limitación se ha eliminado mediante la __VA_OPT__macro funcional. La __VA_OPT__macro 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 las macros en el argumento __VA_OPT__se expandan recursivamente. Sin embargo, es posible sortear esta limitación hasta un número fijo arbitrario de expansiones recursivas. [ 7 ]

Apoyo

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 actualización 2 (versión C++ 5.3). [ 9 ] GCC también admite dichas macros al compilar Objective-C .

Se ha añadido soporte para que la __VA_OPT__macro admita cero argumentos en GNU Compiler Collection 8, [ 10 ] Clang 6, [ 11 ] y Visual Studio 2019 . [ 12 ]

Ejemplo

Si se deseara una funciónprintf similar que tomara como argumentos el archivo y el número de línea desde el que se llama, se aplica la siguiente solución.dbgprintf()

Nuestra función implementada:

void debugPrint ( const char * archivo , int línea , const char * fmt , ...);

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, ...) debugPrint(__FILE__, __LINE__, cformat, __VA_ARGS__)

La razón es que

dbgprintf ( "Hola" )

se expande a

debugPrint ( __FILE__ , __LINE__ , "Hola" , )

donde la coma antes de la llave de cierre dará como resultado un error de sintaxis.

GNU C++ admite una extensión no portable que resuelve este problema:

#define dbgprintf(cformat, ...) debugPrint(__FILE__, __LINE__, cformat, ##__VA_ARGS__)

C++20 admite la siguiente sintaxis.

#define dbgprintf(cformat, ...) debugPrint(__FILE__, __LINE__, cformat __VA_OPT__(,) __VA_ARGS__)

Al usar la cformatcadena como parte de los argumentos variables, podemos sortear las incompatibilidades mencionadas anteriormente. Esto es complejo, pero portable.

#define dbgprintf(...) debugPrint(__FILE__, __LINE__, __VA_ARGS__)

dbgprintf()podría entonces llamarse como

dbgprintf ( "Hola, mundo" );

que se expande a

debugPrint ( __FILE__ , __LINE__ , "Hola, mundo" );

Otro ejemplo es:

dbgprintf ( "%d + %d = %d" , 2 , 2 , 5 );

que se expande a

debugPrint ( __FILE__ , __LINE__ , "%d + %d = %d" , 2 , 2 , 5 );

Sin macros variádicas, no es posible escribir printfdirectamente funciones envolventes. La solución habitual consiste en utilizar la funcionalidad stdargs de C/C++ y realizar la llamada a la función vprintfen su lugar.

coma final

Existe un problema de portabilidad al generar 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 colocar ##delante 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 );

lo cual es equivalente a

fprintf ( stderr , "%s(%u): Demasiados globos %u \n " , __FILE__ , __LINE__ , 42 );

Pero fíjense en 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 ]

Alternativas

Antes de la existencia de argumentos variables en C99, era bastante común usar paréntesis doblemente anidados para aprovechar el número variable de argumentos que se podían proporcionar a la printf()función:

#define dbgprintf(x) debugPrint x

dbgprintf()podría entonces llamarse como:

dbgprintf (( "Hola, mundo %d" , 27 ));

que se expande a:

debugPrint ( "Hola, mundo %d" , 27 );

Óxido

En Rust , una interfaz variádica (también conocida como macro variádica) [ 13 ] permite tomar un número arbitrario de argumentos. Rust carece de funciones variádicas y plantillas variádicas , por lo que la única forma de lograr parámetros variádicos es a través de dichas interfaces variádicas, que utilizan patrones de repetición a través de macro_rules!. Por ejemplo, en , denota un patrón repetitivo, denota un separador, denota cero o más repeticiones (una estrella de Kleene , mientras que que coincide con una o más sería un signo más de Kleene ).$(...),*$(),*+

Un ejemplo de implementación de una macro variádica es el siguiente, equivalente a vec!:

// Define una macro llamada "make_vec!" macro_rules! make_vec { // Coincide con cualquier expresión de Rust // y repite el patrón separado por comas, cero o más veces ( $( $x : expr ), * ) => { { // Vec debe estar completamente calificado // para evitar que se resuelva a un Vec definido por el usuario let mut temp = :: std :: vec :: Vec :: new (); // Repite un push a temp una vez por argumento $( temp . push ( $x ); ) * // Devuelve temp desde la macro temp } }; }

Referencias

  1. Nelson, Clark (16 de julio de 2004). "Cambios en el borrador de trabajo para la sincronización del preprocesador C99" . www.open-std.org . Recuperado el 30 de octubre de 2025 .
  2. "Omisión y supresión de comas" . 18 de junio de 2017. Consultado el 24 de diciembre de 2022 .
  3. 1 2 "WG14 - N3033 : Omisión y supresión de comas" . 2022-07-20. 
  4. 1 2 3 4 Macros variádicas: uso de la colección de compiladores GNU (GCC)
  5. Laurent Deniau (16 de enero de 2006). "__VA_NARG__" . Grupo de noticias : comp.std.c. Usenet: dqgm2f$ije$1@sunnews.cern.ch .  
  6. 1 2 3 Macros variádicas (C++)
  7. Macros recursivas con C++20 __VA_OPT__
  8. Cambio en el código fuente de Clang que menciona la compatibilidad con __VA_ARGS__ (29/07/2006); tenga en cuenta que Clang se publicó como código abierto en 2007. http://llvm.org/viewvc/llvm-project?view=revision&revision=38770
  9. Comparación de características de Sun Studio – http://developers.sun.com/sunstudio/support/CCcompare.html
  10. "Soporte para C++2a en GCC" . Consultado el 14 de junio de 2018 .
  11. "Soporte de C++ en Clang" . Consultado el 14 de junio de 2018 .
  12. "Descripción general del nuevo preprocesador de MSVC" . 10 de septiembre de 2020. Consultado el 8 de diciembre de 2020 .
  13. El equipo de Rust (1 de abril de 2026). "Interfaces variádicas" . rust-lang.org . Rust por ejemplo.

Véase también