Surcharger la macro sur le nombre d'arguments


184

J'ai deux macros FOO2 et FOO3:

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

Je souhaite définir une nouvelle macro FOO comme suit:

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

Mais cela ne fonctionne pas car les macros ne surchargent pas sur le nombre d'arguments.

Sans modifier FOO2et FOO3, existe-t-il un moyen de définir une macro FOO(en utilisant __VA_ARGS__ou autrement) pour obtenir le même effet de distribution FOO(x,y)vers FOO2et FOO(x,y,z)vers FOO3?


1
J'ai le sentiment très fort que cela a été demandé plusieurs fois avant ... [mise à jour] par exemple ici .
Kerrek SB

1
@KerrekSB: Cela peut être lié, ce n'est certainement pas une dupe.
Andrew Tomazos

Non, peut-être pas celui-là, mais quelque chose comme ça arrive environ une fois par mois ...
Kerrek SB

Idem pour C ++: stackoverflow.com/questions/3046889/… Devrait être la même puisque les préprocesseurs sont fondamentalement les mêmes: stackoverflow.com/questions/5085533/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:


265

Simple comme:

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

Donc, si vous avez ces macros:

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

Si vous en voulez un quatrième:

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

Naturellement, si vous définissez FOO2, FOO3et FOO4, la sortie sera remplacée par celles des macros définies.


5
@ Uroc327 L'ajout d'une macro à 0 argument à la liste est possible, voir ma réponse.
augurar

7
Ne fonctionne pas sur Microsoft Visual Studio 2010, VA_ARGS semble être développé en un seul argument de macro.
Étienne

9
J'ai trouvé cette réponse pour le faire fonctionner sous MSVC 2010.
Étienne

8
Si quelqu'un ne sait pas comment utiliser le EXPANDlien mentionné dans @ Étienne, vous l'invoquez essentiellement GET_MACROcomme tel #define FOO(...) EXPAND(GET_MACRO(__VA_ARGS__, FOO3, FOO2, FOO1)(__VA_ARGS__))et il devrait s'étendre au bon nombre d'arguments dans msvc.
vexe le

3
Notez que le C ++ 11, vous obtiendrez un avertissement: ISO C++11 requires at least one argument for the "..." in a variadic macro. Pour résoudre ce problème, ajoutez un argument inutilisé (ou même juste une virgule) après le dernier paramètre dans la définition de FOO (...): #define FOO(...) GET_MACRO(__VA_ARGS__, FOO3, FOO2, UNUSED)(__VA_ARGS__)( Voir qu'il s'exécute sur Coliru ).
métal

49

Pour ajouter à la réponse de netcoder , vous POUVEZ en fait le faire avec une macro à 0 argument, à l'aide de l' ##__VA_ARGS__extension GCC :

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

1
est-il possible d'autoriser FOO1 et FOO2 mais pas FOO0 sans faire #define FOO0 _Pragma("error FOO0 not allowed")?
noɥʇʎԀʎzɐɹƆ

FOO0ne fonctionne pas dans qt + mingw32, l'appel FOO0invoquera leFOO1
JustWe

Très prometteur et simple. Mais ne fonctionne pas pour FOO0 avec -std = c ++ 11 ... :-(
leonp

1
Même problème si vous faites cela en C et que vous essayez d'utiliser -std=c99ou -std=c11. Vous devez utiliser -std=gnu99ou à la -std=gnu11place
Michael Mrozek

1
Il semble que le remplacement _0, ##__VA_ARGS__par _0 __VA_OPT__(,) __VA_ARGS__est la nouvelle façon de procéder.
Wrzlprmft

36

Voici une solution plus générale:

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

Définissez vos fonctions:

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

Vous pouvez maintenant utiliser FOOavec 2, 3 et 4 arguments:

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

Limites

  • Seulement jusqu'à 63 arguments (mais extensible)
  • Fonction pour aucun argument uniquement dans GCC possible

Idées

Utilisez-le pour les arguments par défaut:

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

Utilisez-le pour les fonctions avec un nombre infini d'arguments possible:

#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__est copié de Laurent Deniau & Roland Illig ici: https://groups.google.com/group/comp.std.c/browse_thread/thread/77ee8c8f92e4a3fb/346fc464319b1ee5?pli=1




La macro __NARG_I_semble totalement inutile et superflue. Cela ajoute juste une étape supplémentaire et de la confusion. Je recommande de le supprimer entièrement et simplement définir la __NARG__place que: #define __NARG__(...) __ARG_N(__VA_ARGS__,__RSEQ_N()).
Gabriel Staples

Ou est-ce que cela interrompra d'une manière ou d'une autre le prétraitement? Est-ce que je manque quelque chose?
Gabriel Staples

Idem _VFUNC_: supprimez-le simplement. Ensuite, définissez _VFUNCcomme: #define _VFUNC(name, n) name##nau lieu de #define _VFUNC(name, n) _VFUNC_(name, n).
Gabriel Staples

15

J'étais en train de faire des recherches moi-même, et je suis tombé dessus ici . L'auteur a ajouté la prise en charge des arguments par défaut pour les fonctions C via des macros.

Je vais essayer de résumer brièvement l'article. Fondamentalement, vous devez définir une macro qui peut compter les arguments. Cette macro renverra 2, 1, 0 ou toute plage d'arguments qu'elle peut prendre en charge. Par exemple:

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

Avec cela, vous devez créer une autre macro qui prend un nombre variable d'arguments, compte les arguments et appelle la macro appropriée. J'ai pris votre exemple de macro et l'ai combiné avec l'exemple de l'article. J'ai la fonction d'appel FOO1 a () et la fonction d'appel FOO2 a avec l'argument b (évidemment, je suppose C ++ ici, mais vous pouvez changer la macro en n'importe quoi).

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

Donc si vous avez

FOO(a)
FOO(a,b)

Le préprocesseur étend cela à

a();
a(b);

Je lirais certainement l'article que j'ai lié. C'est très instructif et il mentionne que NARG2 ne fonctionnera pas sur des arguments vides. Il suit ceci ici .


7

Voici une version plus compacte de la réponse ci-dessus . Avec l'exemple.

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

Courir:

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

Notez qu'avoir les deux _OVRet _OVR_EXPANDpeut sembler redondant, mais il est nécessaire que le préprocesseur développe la _COUNT_ARGS(__VA_ARGS__)partie, qui est sinon traitée comme une chaîne.


J'aime cette solution. Peut-il être modifié pour gérer une macro surchargée qui ne prend aucun argument?
Andrew du

3

Vous pouvez peut-être utiliser cette macro pour compter le nombre d'arguments .

#define VA_NUM_ARGS(...) VA_NUM_ARGS_IMPL(__VA_ARGS__, 5,4,3,2,1)
#define VA_NUM_ARGS_IMPL(_1,_2,_3,_4,_5,N,...) N

2

Voici un spin-off de la réponse d'Evgeni Sergeev. Celui-ci prend également en charge les surcharges d'argument nulles !

J'ai testé cela avec GCC et MinGW. Il devrait fonctionner avec les anciennes et les nouvelles versions de C ++. Notez que je ne le garantirais pas pour MSVC ... Mais avec quelques ajustements, je suis convaincu que cela pourrait aussi fonctionner avec cela.

Je l'ai également formaté pour être collé dans un fichier d'en-tête (que j'ai appelé macroutil.h). Si vous faites cela, vous pouvez simplement inclure cet en-tête tout ce dont vous avez besoin, et ne pas regarder la méchanceté impliquée dans l'implémentation.

#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

Cela semble fonctionner correctement sur GCC, Clang et MSVC. C'est une version nettoyée de certaines des réponses ici

#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 Comment ajuster cette macro pour qu'elle fonctionne avec zéro argument #define func0() foo? La version actuelle ne gère malheureusement pas ce cas.
Jerry Ma
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.