Macro de sobrecarga en número de argumentos


184

Tengo dos macros FOO2y FOO3:

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

Quiero definir una nueva macro de la FOOsiguiente manera:

#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 en la cantidad de argumentos.

Sin modificar FOO2y FOO3, ¿hay alguna forma de definir una macro FOO(usando __VA_ARGS__o de otra manera) para obtener el mismo efecto de despachar FOO(x,y)a FOO2, y FOO(x,y,z)a FOO3?


1
Tengo la fuerte sensación de que esto se ha preguntado varias veces antes ... [actualización] por ejemplo, aquí .
Kerrek SB

1
@KerrekSB: Eso puede estar relacionado, sin duda, no es un engaño.
Andrew Tomazos

No, tal vez no ese, pero algo así surge una vez al mes ...
Kerrek SB

Lo mismo para C ++: stackoverflow.com/questions/3046889/… Debería ser el mismo ya que los preprocesadores son básicamente los mismos: stackoverflow.com/questions/5085533/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:


265

Simple como:

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

Entonces, si tienes estas macros:

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

Si quieres un cuarto:

#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)          # expeands to FOO4(a,b,c,d)

Naturalmente, si define FOO2, FOO3y FOO4, la salida será reemplazada por las de las macros definidas.


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

77
No funciona en Microsoft Visual Studio 2010, VA_ARGS parece expandirse en un solo argumento macro.
Étienne

9
Encontré esta respuesta para que funcione bajo MSVC 2010.
Étienne

8
Si alguien está confundido sobre cómo usar lo EXPANDmencionado en el enlace de @Etienne, básicamente lo invocas GET_MACROasí #define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__))y debería expandirse al número correcto de argumentos en msvc.
vexe

3
Tenga en cuenta que en C ++ 11, obtendrá 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__)( Vea cómo se ejecuta en Coliru ).
metal

49

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

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

1
¿es posible permitir FOO1 y FOO2 pero no FOO0 sin hacerlo #define FOO0 _Pragma("error FOO0 not allowed")?
noɥʇʎԀʎzɐɹƆ

FOO0no funciona en qt + mingw32, la llamada FOO0invocará elFOO1
JustWe

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

1
El mismo problema si está haciendo esto en C e intenta usar -std=c99o -std=c11. Necesitas usar -std=gnu99o en su -std=gnu11lugar
Michael Mrozek

1
Parece que reemplazar _0, ##__VA_ARGS__con _0 __VA_OPT__(,) __VA_ARGS__es la nueva forma de hacer esto.
Wrzlprmft

36

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

Define tus 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 puede usar FOOcon 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 ampliable)
  • Función para ningún argumento solo en GCC posible

Ideas

Úselo para 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 número infinito posible 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 y Roland Illig aquí: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1




La macro __NARG_I_parece completamente innecesaria y superflua. Simplemente 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 Staples

¿O eso interrumpirá de alguna manera el preprocesamiento? ¿Me estoy perdiendo de algo?
Gabriel Staples

Lo mismo con _VFUNC_: simplemente elimínelo. Luego, defina _VFUNCcomo: en #define _VFUNC(name, n) name##nlugar de #define _VFUNC(name, n) _VFUNC_(name, n).
Gabriel Staples

15

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

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

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

Con esto, debe crear otra macro que tome un número variable de argumentos, cuente los argumentos y llame a la macro apropiada. Tomé tu 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, supongo que C ++ aquí, pero puede 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 lo expande a

a();
a(b);

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


7

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 _OVRy _OVR_EXPANDpuede parecer redundante, pero es necesario que el preprocesador expanda la _COUNT_ARGS(__VA_ARGS__)parte, que de lo contrario se trata como una cadena.


Me gusta esta solución ¿Se puede modificar para manejar una macro sobrecargada que toma cero argumentos?
Andrew


2

Aquí hay un spin off de la respuesta de Evgeni Sergeev. ¡Este también admite sobrecargas de argumento cero !

Probé esto con GCC y MinGW. Debería funcionar con versiones antiguas y nuevas de C ++. Tenga en cuenta que no lo garantizaría para MSVC ... Pero con algunos ajustes, estoy seguro de que también podría funcionar.

También formateé esto para pegarlo en un archivo de encabezado (que llamé macroutil.h). Si hace eso, puede incluir este encabezado según lo que necesite la función, y no mirar la maldad involucrada en la implementación.

#ifndef MACROUTIL_H
#define MACROUTIL_H

//-----------------------------------------------------------------------------
// OVERLOADED_MACRO
//
// used to create other macros with overloaded argument lists
//
// Example Use:
// #define myMacro(...) OVERLOADED_MACRO( myMacro, __VA_ARGS__ )
// #define myMacro0() someFunc()
// #define myMacro1( arg1 ) someFunc( arg1 )
// #define myMacro2( arg1, arg2 ) someFunc( arg1, arg2 )
//
// myMacro();
// myMacro(1);
// myMacro(1,2);
//
// Note the numerical suffix on the macro names,
// which indicates the number of arguments.
// That is the REQUIRED naming convention for your macros.
//
//-----------------------------------------------------------------------------

// OVERLOADED_MACRO
// derived from: /programming/11761703/overloading-macro-on-number-of-arguments
// replaced use of _COUNT_ARGS macro with VA_NUM_ARGS defined below
// to support of zero argument overloads
#define OVERLOADED_MACRO(M, ...) _OVR(M, VA_NUM_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__, 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1)
#define _ARG_PATTERN_MATCH(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15, N, ...)   N

// VA_NUM_ARGS
// copied from comments section of:
// http://efesx.com/2010/07/17/variadic-macro-to-count-number-of-arguments/
// which itself was derived from:
// https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/
#define _ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
#define HAS_COMMA(...) _ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
#define HAS_NO_COMMA(...) _ARG16(__VA_ARGS__, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
#define _TRIGGER_PARENTHESIS_(...) ,

#define HAS_ZERO_OR_ONE_ARGS(...) \
    _HAS_ZERO_OR_ONE_ARGS( \
    /* test if there is just one argument, eventually an empty one */ \
    HAS_COMMA(__VA_ARGS__), \
    /* test if _TRIGGER_PARENTHESIS_ together with the argument adds a comma */ \
    HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
    /* test if the argument together with a parenthesis adds a comma */ \
    HAS_COMMA(__VA_ARGS__ (~)), \
    /* test if placing it between _TRIGGER_PARENTHESIS_ and the parenthesis adds a comma */ \
    HAS_COMMA(_TRIGGER_PARENTHESIS_ __VA_ARGS__ (~)) \
    )

#define PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
#define _HAS_ZERO_OR_ONE_ARGS(_0, _1, _2, _3) HAS_NO_COMMA(PASTE5(_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define _IS_EMPTY_CASE_0001 ,

#define _VA0(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA1(...) HAS_ZERO_OR_ONE_ARGS(__VA_ARGS__)
#define _VA2(...) 2
#define _VA3(...) 3
#define _VA4(...) 4
#define _VA5(...) 5
#define _VA6(...) 6
#define _VA7(...) 7
#define _VA8(...) 8
#define _VA9(...) 9
#define _VA10(...) 10
#define _VA11(...) 11
#define _VA12(...) 12
#define _VA13(...) 13
#define _VA14(...) 14
#define _VA15(...) 15
#define _VA16(...) 16

#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, PP_RSEQ_N(__VA_ARGS__) )
#define VA_NUM_ARGS_IMPL(...) VA_NUM_ARGS_N(__VA_ARGS__)

#define VA_NUM_ARGS_N( \
    _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
    _11,_12,_13,_14,_15,_16,N,...) N

#define PP_RSEQ_N(...) \
    _VA16(__VA_ARGS__),_VA15(__VA_ARGS__),_VA14(__VA_ARGS__),_VA13(__VA_ARGS__), \
    _VA12(__VA_ARGS__),_VA11(__VA_ARGS__),_VA10(__VA_ARGS__), _VA9(__VA_ARGS__), \
    _VA8(__VA_ARGS__),_VA7(__VA_ARGS__),_VA6(__VA_ARGS__),_VA5(__VA_ARGS__), \
    _VA4(__VA_ARGS__),_VA3(__VA_ARGS__),_VA2(__VA_ARGS__),_VA1(__VA_ARGS__), \
    _VA0(__VA_ARGS__)

//-----------------------------------------------------------------------------

#endif // MACROUTIL_H

2

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

1
@RianQuinn ¿Cómo ajustar esta macro para que funcione con argumento cero #define func0() foo? La versión actual no maneja este caso desafortunadamente.
Jerry Ma
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.