Sobrecarga de macro en número de argumentos

9 minutos de lectura

avatar de usuario
Andrés Tomazos

tengo dos macros FOO2 y FOO3:

#define FOO2(x,y) ...
#define FOO3(x,y,z) ...

Quiero definir una nueva macro FOO como sigue:

#define FOO(x,y) FOO2(x,y)
#define FOO(x,y,z) FOO3(x,y,z)

Pero esto no funciona porque las macros no se sobrecargan con la cantidad de argumentos.

sin modificar FOO2 y FOO3hay alguna forma de definir una macro FOO (utilizando __VA_ARGS__ o de otro modo) para obtener el mismo efecto de enviar FOO(x,y) para FOO2y FOO(x,y,z) para FOO3?

  • Tengo la fuerte sensación de que esto se ha preguntado varias veces antes… [update] por ejemplo aquí.

    – KerrekSB

    1 de agosto de 2012 a las 14:45


  • @KerrekSB: Eso puede estar relacionado, pero ciertamente no es un engaño.

    – Andrés Tomazos

    1 de agosto de 2012 a las 15:16

  • No, tal vez no ese, pero algo como esto aparece una vez al mes…

    – KerrekSB

    1 de agosto de 2012 a las 15:18

  • Lo mismo para C++: stackoverflow.com/questions/3046889/… Debería ser lo mismo ya que los preprocesadores son básicamente los mismos: stackoverflow.com/questions/5085533/…

    – Ciro Santilli Путлер Капут 六四事

    20 de junio de 2015 a las 22:03

  • Relacionado: stackoverflow.com/questions/11317474/…

    – Gabriel grapas

    5 de marzo de 2020 a las 20:32

avatar de usuario
augurar

Para agregar a la respuesta de netcoder, de hecho PUEDE hacer esto con una macro de 0 argumentos, con la ayuda de GCC ##__VA_ARGS__ extensión:

#define GET_MACRO(_0, _1, _2, NAME, ...) NAME
#define FOO(...) GET_MACRO(_0, ##__VA_ARGS__, FOO2, FOO1, FOO0)(__VA_ARGS__)

  • ¿Es posible permitir FOO1 y FOO2 pero no FOO0 sin hacer #define FOO0 _Pragma("error FOO0 not allowed")?

    – noɥʇʎ ʎzɐɹƆ

    24 de julio de 2017 a las 22:24

  • FOO0 no funciona en qt + mingw32, llame FOO0 invocará el FOO1

    – Solo nosotros

    8 de marzo de 2018 a las 6:35


  • Muy prometedor y simple. Pero no funciona para FOO0 con -std=c++11…:-(

    – Leonp

    9 de agosto de 2019 a las 18:53

  • El mismo problema si está haciendo esto en C e intenta usar -std=c99 o -std=c11. necesitas usar -std=gnu99 o -std=gnu11 en cambio

    – Michael Mrozek

    11 de febrero de 2020 a las 4:45

  • Parece que reemplazando _0, ##__VA_ARGS__ con _0 __VA_OPT__(,) __VA_ARGS__ es la nueva forma de hacer esto.

    – Wrzlprmft

    1 de abril de 2020 a las 8:01

avatar de usuario
codificador de red

simple como:

#define GET_MACRO(_1,_2,_3,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2)(__VA_ARGS__)

Entonces, si tiene estas macros, se expanden como se describe:

FOO(World, !)         // expands to FOO2(World, !)
FOO(foo,bar,baz)      // expands to FOO3(foo,bar,baz)

Si quieres una cuarta:

#define GET_MACRO(_1,_2,_3,_4,NAME,...) NAME
#define FOO(...) GET_MACRO(__VA_ARGS__, FOO4, FOO3, FOO2)(__VA_ARGS__)

FOO(a,b,c,d)          // expands to FOO4(a,b,c,d)

Naturalmente, si se define FOO2, FOO3 y FOO4la salida será reemplazada por las de las macros definidas.

  • @ Uroc327 Es posible agregar una macro de 0 argumentos a la lista, vea mi respuesta.

    – augurar

    27 de enero de 2014 a las 0:59

  • No funciona en Microsoft Visual Studio 2010, VA_ARGS parece expandirse en un único argumento macro.

    – Étienne

    28/10/2014 a las 11:32

  • Encontré esta respuesta para que funcione en MSVC 2010.

    – Étienne

    28 de octubre de 2014 a las 14:14

  • Si alguien está confundido acerca de cómo usar el EXPAND mencionado en el enlace de @Étienne, básicamente lo invocas en GET_MACRO al igual que #define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__)) y debería expandirse al número correcto de argumentos en msvc.

    – enfadar

    18/09/2015 a las 16:01

  • Tenga en cuenta que en C++ 11, recibirá una advertencia: ISO C++11 requires at least one argument for the "..." in a variadic macro. Para solucionar esto, agregue un argumento no utilizado (o incluso una coma) después del último parámetro en la definición de FOO(…): #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2, UNUSED)(__VA_ARGS__) (Míralo correr en Coliru).

    – metal

    12 de abril de 2017 a las 12:26


avatar de usuario
rian quinn

Esto parece funcionar bien en GCC, Clang y MSVC. Es una versión limpia de algunas de las respuestas aquí.

#define _my_BUGFX(x) x

#define _my_NARG2(...) _my_BUGFX(_my_NARG1(__VA_ARGS__,_my_RSEQN()))
#define _my_NARG1(...) _my_BUGFX(_my_ARGSN(__VA_ARGS__))
#define _my_ARGSN(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,N,...) N
#define _my_RSEQN() 10,9,8,7,6,5,4,3,2,1,0

#define _my_FUNC2(name,n) name ## n
#define _my_FUNC1(name,n) _my_FUNC2(name,n)
#define GET_MACRO(func,...) _my_FUNC1(func,_my_BUGFX(_my_NARG2(__VA_ARGS__))) (__VA_ARGS__)

#define FOO(...) GET_MACRO(FOO,__VA_ARGS__)

  • @RianQuinn Cómo ajustar esta macro para que funcione sin argumentos #define func0() foo? Lamentablemente, la versión actual no maneja este caso.

    – Jerry Mamá

    9 de abril de 2019 a las 17:02


avatar de usuario
Evgeni Serguéiev

Aquí hay una versión más compacta de la respuesta anterior. Con ejemplo.

#include <iostream>
using namespace std;

#define OVERLOADED_MACRO(M, ...) _OVR(M, _COUNT_ARGS(__VA_ARGS__)) (__VA_ARGS__)
#define _OVR(macroName, number_of_args)   _OVR_EXPAND(macroName, number_of_args)
#define _OVR_EXPAND(macroName, number_of_args)    macroName##number_of_args

#define _COUNT_ARGS(...)  _ARG_PATTERN_MATCH(__VA_ARGS__, 9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9, N, ...)   N


//Example:
#define ff(...)     OVERLOADED_MACRO(ff, __VA_ARGS__)
#define ii(...)     OVERLOADED_MACRO(ii, __VA_ARGS__)

#define ff3(c, a, b) for (int c = int(a); c < int(b); ++c)
#define ff2(c, b)   ff3(c, 0, b)

#define ii2(a, b)   ff3(i, a, b)
#define ii1(n)      ii2(0, n)


int main() {
    ff (counter, 3, 5)
        cout << "counter = " << counter << endl;
    ff (abc, 4)
        cout << "abc = " << abc << endl;
    ii (3)
        cout << "i = " << i << endl;
    ii (100, 103)
        cout << "i = " << i << endl;


    return 0;
}

Correr:

User@Table 13:06:16 /c/T
$ g++ test_overloaded_macros.cpp 

User@Table 13:16:26 /c/T
$ ./a.exe
counter = 3
counter = 4
abc = 0
abc = 1
abc = 2
abc = 3
i = 0
i = 1
i = 2
i = 100
i = 101
i = 102

Tenga en cuenta que tener ambos _OVR y _OVR_EXPAND puede parecer redundante, pero es necesario que el preprocesador amplíe el _COUNT_ARGS(__VA_ARGS__) parte, que de lo contrario se trata como una cadena.

avatar de usuario
R1tschY

Aquí hay una solución más general:

// get number of arguments with __NARG__
#define __NARG__(...)  __NARG_I_(__VA_ARGS__,__RSEQ_N())
#define __NARG_I_(...) __ARG_N(__VA_ARGS__)
#define __ARG_N( \
      _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
     _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
     _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
     _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
     _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
     _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
     _61,_62,_63,N,...) N
#define __RSEQ_N() \
     63,62,61,60,                   \
     59,58,57,56,55,54,53,52,51,50, \
     49,48,47,46,45,44,43,42,41,40, \
     39,38,37,36,35,34,33,32,31,30, \
     29,28,27,26,25,24,23,22,21,20, \
     19,18,17,16,15,14,13,12,11,10, \
     9,8,7,6,5,4,3,2,1,0

// general definition for any function name
#define _VFUNC_(name, n) name##n
#define _VFUNC(name, n) _VFUNC_(name, n)
#define VFUNC(func, ...) _VFUNC(func, __NARG__(__VA_ARGS__)) (__VA_ARGS__)

// definition for FOO
#define FOO(...) VFUNC(FOO, __VA_ARGS__)

Defina sus funciones:

#define FOO2(x, y) ((x) + (y))
#define FOO3(x, y, z) ((x) + (y) + (z))

// it also works with C functions:
int FOO4(int a, int b, int c, int d) { return a + b + c + d; }

Ahora puedes usar FOO con 2, 3 y 4 argumentos:

FOO(42, 42) // will use makro function FOO2
FOO(42, 42, 42) // will use makro function FOO3
FOO(42, 42, 42, 42) // will call FOO4 function

Limitaciones

  • Solo hasta 63 argumentos (pero expandible)
  • Función sin argumento solo en GCC posible

Ideas

Úselo para los argumentos predeterminados:

#define func(...) VFUNC(func, __VA_ARGS__)
#define func2(a, b) func4(a, b, NULL, NULL)
#define func3(a, b, c) func4(a, b, c, NULL)

// real function:
int func4(int a, int b, void* c, void* d) { /* ... */ }

Úselo para funciones con un posible número infinito de argumentos:

#define SUM(...) VFUNC(SUM, __VA_ARGS__)
#define SUM2(a, b) ((a) + (b))
#define SUM3(a, b, c) ((a) + (b) + (c))
#define SUM4(a, b, c) ((a) + (b) + (c) + (d))
// ...

PD: __NARG__ se copia de Laurent Deniau & Roland Illig aquí: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1

  • Relacionado: stackoverflow.com/questions/11317474/…

    – Gabriel grapas

    5 de marzo de 2020 a las 20:32

  • Este también: stackoverflow.com/questions/2124339/…

    – Gabriel grapas

    5 de marzo de 2020 a las 20:40

  • la macro __NARG_I_ parece completamente innecesario y superfluo. Solo agrega un paso adicional y confusión. Recomiendo eliminarlo por completo y simplemente definir __NARG__ en cambio como: #define __NARG__(...) __ARG_N(__VA_ARGS__,__RSEQ_N()).

    – Gabriel grapas

    26 mayo 2020 a las 22:51


  • ¿O eso de alguna manera romperá el preprocesamiento? ¿Me estoy perdiendo de algo?

    – Gabriel grapas

    26 mayo 2020 a las 22:55

  • Lo mismo con _VFUNC_: solo bórralo. Luego, define _VFUNC como: #define _VFUNC(name, n) name##n en vez de #define _VFUNC(name, n) _VFUNC_(name, n).

    – Gabriel grapas

    26 mayo 2020 a las 22:58

avatar de usuario
Yura.S

Según la respuesta de @netcoder y la sugerencia de @vexe sobre la compatibilidad con el compilador de Visual Studio, encontré que este código funciona bastante bien en varias plataformas:

#define FOO1(a) func1(a)
#define FOO2(a, b) func2(a, b)
#define FOO3(a, b, c) func3(a, b, c)

#define EXPAND(x) x
#define GET_MACRO(_1, _2, _3, NAME, ...) NAME
#define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__))

donde func1(), func2(), func3() son solo funciones normales que aceptan diferentes números de parámetros.

  • Relacionado: stackoverflow.com/questions/11317474/…

    – Gabriel grapas

    5 de marzo de 2020 a las 20:32

  • Este también: stackoverflow.com/questions/2124339/…

    – Gabriel grapas

    5 de marzo de 2020 a las 20:40

  • la macro __NARG_I_ parece completamente innecesario y superfluo. Solo agrega un paso adicional y confusión. Recomiendo eliminarlo por completo y simplemente definir __NARG__ en cambio como: #define __NARG__(...) __ARG_N(__VA_ARGS__,__RSEQ_N()).

    – Gabriel grapas

    26 mayo 2020 a las 22:51


  • ¿O eso de alguna manera romperá el preprocesamiento? ¿Me estoy perdiendo de algo?

    – Gabriel grapas

    26 mayo 2020 a las 22:55

  • Lo mismo con _VFUNC_: solo bórralo. Luego, define _VFUNC como: #define _VFUNC(name, n) name##n en vez de #define _VFUNC(name, n) _VFUNC_(name, n).

    – Gabriel grapas

    26 mayo 2020 a las 22:58

avatar de usuario
enorme

Estaba investigando esto yo mismo, y me encontré con esto aquí. El autor agregó compatibilidad con argumentos predeterminados para funciones de C a través de macros.

Intentaré resumir brevemente el artículo. Básicamente, necesita definir una macro que pueda contar argumentos. Esta macro devolverá 2, 1, 0 o cualquier rango de argumentos que pueda admitir. P.ej:

#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)

Con esto, necesita crear otra macro que tome un número variable de argumentos, cuente los argumentos y llame a la macro apropiada. Tomé su macro de ejemplo y la combiné con el ejemplo del artículo. Tengo la función de llamada FOO1 a() y la función de llamada FOO2 a con el argumento b (obviamente, asumo C++ aquí, pero puedes cambiar la macro a lo que sea).

#define FOO1(a) a();
#define FOO2(a,b) a(b);

#define _ARG2(_0, _1, _2, ...) _2
#define NARG2(...) _ARG2(__VA_ARGS__, 2, 1, 0)

#define _ONE_OR_TWO_ARGS_1(a) FOO1(a)
#define _ONE_OR_TWO_ARGS_2(a, b) FOO2(a,b)

#define __ONE_OR_TWO_ARGS(N, ...) _ONE_OR_TWO_ARGS_ ## N (__VA_ARGS__)
#define _ONE_OR_TWO_ARGS(N, ...) __ONE_OR_TWO_ARGS(N, __VA_ARGS__)

#define FOO(...) _ONE_OR_TWO_ARGS(NARG2(__VA_ARGS__), __VA_ARGS__)

entonces si tienes

FOO(a)
FOO(a,b)

El preprocesador expande eso para

a();
a(b);

Definitivamente leería el artículo que vinculé. Es muy informativo y menciona que NARG2 no funcionará con argumentos vacíos. Él sigue esto aquí.

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad