stringtranslate.com

Macro variádica en el preprocesador C

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]

Sintaxis de la declaración

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]

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 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]

Ejemplo

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()

  1. Nuestra función implementada
    void realdbgprintf ( const char * NombreArchivoOrigen , int NombreLíneaOrigen , const char * CadenaFormatC , ...);          
  2. 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" , )    
    donde la coma antes de la llave de cierre dará como resultado un error de sintaxis.
  3. GNU C++ admite una extensión no portable que resuelve este problema.
     #define dbgprintf(cformat, ...) \  realdbgprintf(__ARCHIVO__, __LÍNEA__, cformat, ##__VA_ARGS__)
  4. C++20 finalmente admite la siguiente sintaxis.
     #define dbgprintf(cformat, ...) \  realdbgprintf (__ARCHIVO__, __LÍNEA__, cformat __VA_OPT__(,) __VA_ARGS__)
  5. Al usar la cadena 'cformat' como parte de los argumentos variádicos podemos

    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 printfdirectamente. La solución alternativa estándar es usar la funcionalidad stdargs de C/C++ y hacer que se llame a la función vprintfen su lugar.

Coma final

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]

Alternativas

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 );  

Referencias

  1. ^ Cambios en el borrador de trabajo para la sincronización del preprocesador C99 – http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1653.htm
  2. ^ "Omisión de comas y eliminación de comas". 18 de junio de 2017. Consultado el 24 de diciembre de 2022 .
  3. ^ ab "WG14 - N3033: Omisión y eliminación de comas". 20 de julio de 2022.
  4. ^ abcd 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:  [email protected].
  6. ^ abc Macros variádicas (C++)
  7. ^ Macros recursivas con C++20 __VA_OPT__
  8. ^ Cambio en el código fuente de Clang que menciona el soporte para __VA_ARGS__ (29 de julio de 2006), tenga en cuenta que Clang se convirtió en 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. ^ "Compatibilidad con C++2a en GCC" . Consultado el 14 de junio de 2018 .
  11. ^ "Compatibilidad con 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 .

Véase también