Can macros be overloaded by number of arguments?
Following is an improvement upon Potatoswatter's answer, which can differentiate between zero and one argument.
In a nutshell, when __VA_ARGS__
is empty, EXPAND __VA_ARGS__ ()
inside VA_SIZE
macro becomes EXPAND ()
and is substituted with 6 commas. So, VA_SIZE...
becomes COMPOSE( GET_COUNT, (,,,,,, , 0, 6, 5, 4, 3, 2, 1) )
, and that becomes GET_COUNT (,,,,,, , 0, 6, 5, 4, 3, 2, 1)
and returns 0.
On the other hand, when __VA_ARGS__
is eg, int, 5
, EXPAND __VA_ARGS__ ()
becomes EXPAND int, 5 ()
. So, VA_SIZE...
becomes COMPOSE( GET_COUNT, (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1) )
, which becomes GET_COUNT (EXPAND int, 5 (), 0, 6, 5, 4, 3, 2, 1)
and returns 2, as described in Potatoswatter's answer.
I got the EXPAND
idea from Jason Dang's answer.
Library code:
#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )
#define COMPOSE( NAME, ARGS ) NAME ARGS
#define GET_COUNT( _0, _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define EXPAND() ,,,,,, // 6 commas (or 7 empty tokens)
#define VA_SIZE( ... ) COMPOSE( GET_COUNT, (EXPAND __VA_ARGS__ (), 0, 6, 5, 4, 3, 2, 1) )
#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)
Usage:
#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ )
#define MY_OVERLOADED_0( ) meh()
#define MY_OVERLOADED_1( X ) foo< X >
#define MY_OVERLOADED_2( X, Y ) bar< X >( Y )
#define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z()
MY_OVERLOADED() // meh()
MY_OVERLOADED(bool) // foo< bool >
MY_OVERLOADED(int, 5) // bar< int >( 5 )
MY_OVERLOADED(me, double, now) // bang_me< double >.now()
(Edit: See the end for a ready-made solution.)
To get an overloaded macro, first we need a macro which selects between several implementations. This part doesn't use a variadic macro. Then a variadic macro which generically counts its arguments produces a selector. Plugging the argument count into a dispatcher produces an overloaded macro.
Caveat: This system cannot tell the difference between zero and one arguments because there is no difference between no argument, and a single empty argument. They both look like MACRO()
.
To select between implementations, use the macro catenation operator with a series of function-like macros.
#define select( selector, ... ) impl ## _ ## selector( __VA_ARGS__ )
#define impl_1() meh
#define impl_2( abc, xyz ) # abc "wizza" xyz()
//etc
// usage: select( 1 ) => impl_1() => meh
// select( 2, huz, bar ) => impl_2( huzza, bar ) => "huz" "wizza" bar()
Because the ##
operator suppresses macro expansion of its arguments, it's better to wrap it in another macro.
#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )
To count arguments, use __VA_ARGS__
to shift arguments like so (this is the clever part):
#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )
Library code:
#define CAT( A, B ) A ## B
#define SELECT( NAME, NUM ) CAT( NAME ## _, NUM )
#define GET_COUNT( _1, _2, _3, _4, _5, _6 /* ad nauseam */, COUNT, ... ) COUNT
#define VA_SIZE( ... ) GET_COUNT( __VA_ARGS__, 6, 5, 4, 3, 2, 1 )
#define VA_SELECT( NAME, ... ) SELECT( NAME, VA_SIZE(__VA_ARGS__) )(__VA_ARGS__)
Usage:
#define MY_OVERLOADED( ... ) VA_SELECT( MY_OVERLOADED, __VA_ARGS__ )
#define MY_OVERLOADED_1( X ) foo< X >
#define MY_OVERLOADED_2( X, Y ) bar< X >( Y )
#define MY_OVERLOADED_3( X, Y, Z ) bang_ ## X< Y >.Z()