Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can macros be overloaded by number of arguments?

How does this work? How can a C99/C++11 variadic macro be implemented to expand to different things on the sole basis of how many arguments are given to it?

like image 742
Potatoswatter Avatar asked May 22 '13 03:05

Potatoswatter


People also ask

How many arguments can a macro have?

For portability, you should not have more than 31 parameters for a macro. The parameter list may end with an ellipsis (…).

Can macros be overloaded?

Macros cannot be overloaded and the (...) mechanism cannot do what you want.

Can you supply more than one argument in a macro call?

Yes. If you try untouchable(foo,first) you get an error because the macro only sees two parameters but was expecting 4.

How many arguments we pass in macro definition?

The macro definition can include macro arguments, which can be assigned specific values in the macro call. There are two types of arguments: keyword and positional. Keyword arguments are assigned names in the macro definition; in the macro call, they are identified by name.


2 Answers

(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() 
like image 169
Potatoswatter Avatar answered Sep 23 '22 16:09

Potatoswatter


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() 
like image 24
Innocent Bystander Avatar answered Sep 22 '22 16:09

Innocent Bystander