我有两个宏FOO2和FOO3:

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

我想定义一个新的宏FOO如下所示:

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

但这是行不通的,因为宏不会重载参数的数量。

在不修改FOO2和FOO3的情况下,是否有某种方法来定义一个宏FOO(使用__VA_ARGS__或其他方式)来获得将FOO(x,y)分配给FOO2和将FOO(x,y,z)分配给FOO3的相同效果?


当前回答

根据@netcoder的回答和@vexe关于Visual Studio编译器支持的建议,我发现这段代码在各种平台上都运行得很好:

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

,其中func1(), func2(), func3()只是普通函数,接受不同数量的形参。

其他回答

简单:

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

所以如果你有这些宏,它们会像描述的那样展开:

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

如果你想要第四个:

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

当然,如果定义了FOO2、FOO3和FOO4,输出将被已定义宏的输出所取代。

这似乎工作得很好GCC, Clang和MSVC。这是一些答案的整理版本

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

也许你可以使用这个宏来计算参数的数量。

#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

根据@netcoder的回答和@vexe关于Visual Studio编译器支持的建议,我发现这段代码在各种平台上都运行得很好:

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

,其中func1(), func2(), func3()只是普通函数,接受不同数量的形参。

下面是Evgeni Sergeev的回答。这个也支持零参数重载!

我用GCC和MinGW进行了测试。它应该适用于旧版本和新版本的c++。请注意,我不能保证MSVC…但经过一些调整,我相信它也可以做到这一点。

我还格式化了它,以便粘贴到一个头文件(我称之为macroutil.h)。如果你这样做了,你可以只包括这个头,无论你需要的特性,而不考虑在实现中涉及的麻烦。

#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: https://stackoverflow.com/questions/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