Sobrecarregando macro no número de argumentos


183

Eu tenho duas macros FOO2e FOO3:

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

Quero definir uma nova macro da FOOseguinte maneira:

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

Mas isso não funciona porque as macros não sobrecarregam o número de argumentos.

Sem modificar FOO2e FOO3, existe alguma maneira de definir uma macro FOO(usando __VA_ARGS__ou não) para obter o mesmo efeito de despacho FOO(x,y)para FOO2e FOO(x,y,z)para FOO3?


1
Tenho uma forte sensação de que isso já foi solicitado várias vezes antes ... [atualizar] por exemplo, aqui .
Kerrek SB

1
@KerrekSB: Isso pode estar relacionado, certamente não é um engano.
Andrew Tomazos

Não, talvez não um, mas algo como isto vem uma vez por mês ...
Kerrek SB

O mesmo para C ++: stackoverflow.com/questions/3046889/… deve ser o mesmo, pois os pré-processadores são basicamente os mesmos: stackoverflow.com/questions/5085533/…
Ciro Santilli

Respostas:


264

Simples como:

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

Então, se você possui essas macros:

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

Se você quer um quarto:

#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, se você definir FOO2, FOO3e FOO4, a saída será substituída pelas das macros definidas.


5
@ Uroc327 É possível adicionar uma macro com 0 argumentos à lista, veja a minha resposta.
Augurar

7
Não funciona no Microsoft Visual Studio 2010, VA_ARGS parece ser expandido em um único argumento de macro.
Étienne

9
Encontrado esta resposta para torná-lo trabalhar sob MSVC 2010.
Étienne

8
Se alguém está confuso sobre como usar o EXPANDlink mencionado no @ Étienne, você basicamente o invoca GET_MACROassim #define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__))e ele deve se expandir para o número certo de argumentos no msvc.
vexe 18/09/2015

3
Note-se que em C ++ 11, você receberá um aviso: ISO C++11 requires at least one argument for the "..." in a variadic macro. Para corrigir isso, adicione um argumento não utilizado (ou mesmo uma vírgula) após o último parâmetro na definição de FOO (...): #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2, UNUSED)(__VA_ARGS__)( Veja em Coliru ).
de metal

49

Para adicionar à resposta do netcoder , você PODE de fato fazer isso com uma macro de 0 argumentos, com a ajuda da ##__VA_ARGS__extensão GCC :

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

1
é possível permitir FOO1 e FOO2, mas não FOO0 sem fazer #define FOO0 _Pragma("error FOO0 not allowed")?
#

FOO0não funciona no qt + mingw32, chamada FOO0irá chamar oFOO1
JustWe

Muito promissor e simples. Mas não funciona para FOO0 com -std = c ++ 11 ... :-(
leonp

1
Mesmo problema se você estiver fazendo isso em C e tentar usar -std=c99ou -std=c11. Você precisa usar -std=gnu99ou em -std=gnu11vez disso
Michael Mrozek

1
Parece que substituir _0, ##__VA_ARGS__por _0 __VA_OPT__(,) __VA_ARGS__é a nova maneira de fazer isso.
Wrzlprmft

36

Aqui está uma solução mais geral:

// 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 suas funções:

#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; }

Agora você pode usar FOOcom 2, 3 e 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

Limitações

  • Apenas até 63 argumentos (mas expansível)
  • Função para nenhum argumento apenas no GCC possível

Ideias

Use-o para argumentos padrão:

#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) { /* ... */ }

Use-o para funções com possível 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))
// ...

PS: __NARG__é copiado de Laurent Deniau & Roland Illig aqui: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1




A macro __NARG_I_parece completamente desnecessária e supérflua. Apenas adiciona uma etapa extra e confusão. Eu recomendo excluí-la inteiramente e apenas definir __NARG__em vez como: #define __NARG__(...) __ARG_N(__VA_ARGS__,__RSEQ_N()).
Gabriel Staples

Ou isso de alguma forma interromperá o pré-processamento? Estou esquecendo de algo?
Gabriel Staples

Mesmo com _VFUNC_: apenas exclua-o. Em seguida, defina _VFUNCcomo: em #define _VFUNC(name, n) name##nvez de #define _VFUNC(name, n) _VFUNC_(name, n).
Gabriel Staples

15

Eu mesmo estava pesquisando isso e me deparei com isso aqui . O autor adicionou suporte a argumentos padrão para funções C via macros.

Vou tentar resumir brevemente o artigo. Basicamente, você precisa definir uma macro que possa contar argumentos. Essa macro retornará 2, 1, 0 ou qualquer intervalo de argumentos que ela possa suportar. Por exemplo:

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

Com isso, você precisa criar outra macro que utilize um número variável de argumentos, conte os argumentos e chame a macro apropriada. Peguei sua macro de exemplo e a combinei com o exemplo do artigo. Eu tenho a função de chamada FOO1 a () e a função de chamada FOO2 a com o argumento b (obviamente, estou assumindo C ++ aqui, mas você pode alterar a macro para o que for).

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

Então se você tem

FOO(a)
FOO(a,b)

O pré-processador expande isso para

a();
a(b);

Eu definitivamente leria o artigo que vinculei. É muito informativo e ele menciona que o NARG2 não funcionará em argumentos vazios. Ele segue isso aqui .


7

Aqui está uma versão mais compacta da resposta acima . Com exemplo.

#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;
}

Corre:

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

Observe que ter os dois _OVRe _OVR_EXPANDpode parecer redundante, mas é necessário que o pré-processador expanda a _COUNT_ARGS(__VA_ARGS__)peça, que de outra forma é tratada como uma sequência.


Eu gosto desta solução. Ele pode ser modificado para manipular uma macro sobrecarregada que aceita zero argumentos?
Andrew Andrew


2

Aqui está um resumo da resposta de Evgeni Sergeev. Este também suporta zero sobrecarga de argumentos !

Eu testei isso com GCC e MinGW. Ele deve funcionar com versões antigas e novas do C ++. Observe que eu não garantiria isso para o MSVC ... Mas, com alguns ajustes, estou confiante de que poderia ser feito para trabalhar com isso também.

Também formatei isso para ser colado em um arquivo de cabeçalho (que chamei macroutil.h). Se você fizer isso, poderá incluir apenas esse cabeçalho o que precisar do recurso, e não olhar para a maldade envolvida na implementação.

#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

Isso parece funcionar bem no GCC, Clang e MSVC. É uma versão limpa de algumas das respostas aqui

#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 Como ajustar essa macro para que funcione com argumento zero #define func0() foo? A versão atual não lida com esse caso, infelizmente.
21719 Jerry Ma
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.