2
votes

Overloading Macro on Number of Arguments

https://codecraft.co/2014/11/25/variadic-macros-tricks/

I've been looking at the two links above, trying to get the following code to work:

#define _GET_NUMBER(_0, _1, _2, _3, _4, _5, NAME, ...) NAME
#define OUTPUT_ARGS_COUNT(...) _GET_NUMBER(_0, ##__VA_ARGS__, 5, 4, 3, 2, 1, 0)

...

cout << OUTPUT_ARGS_COUNT("HelloWorld", 1.2) << endl;
cout << OUTPUT_ARGS_COUNT("HelloWorld") << endl;
cout << OUTPUT_ARGS_COUNT() << endl;

This compiles, runs, and gives the following output:

2
1
1

I can not for the life of me figure out why the call OUTPUT_ARGS_COUNT() is giving me 1 instead of 0. I have an ok understanding of the code I'm trying to use, but it's a tad greek to me still so I guess it's possible that I'm not applying something correctly despite the fact I literally copied and pasted the example code from the link on stack overflow.

I'm compiling using g++ 5.4.0 20160609.

Any ideas or additional resources you can point me to would be greatly appreciated.

2
You can see at gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html: "The above explanation is ambiguous about the case where the only macro parameter is a variable arguments parameter, as it is meaningless to try to distinguish whether no argument at all is an empty argument or a missing argument. CPP retains the comma when conforming to a specific C standard. Otherwise the comma is dropped as an extension to the standard.". - Jarod42
Check this stackoverflow.com/questions/2308243/…, the solution there also fails on zero arguments - Passer By

2 Answers

3
votes

You can see at http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html:

Second, the ‘##’ token paste operator has a special meaning when placed between a comma and a variable argument. If you write

#define eprintf(format, ...) fprintf (stderr, format, ##__VA_ARGS__)

and the variable argument is left out when the eprintf macro is used, then the comma before the ‘##’ will be deleted. This does not happen if you pass an empty argument, nor does it happen if the token preceding ‘##’ is anything other than a comma.

eprintf ("success!\n")
 → fprintf(stderr, "success!\n");

The above explanation is ambiguous about the case where the only macro parameter is a variable arguments parameter, as it is meaningless to try to distinguish whether no argument at all is an empty argument or a missing argument. CPP retains the comma when conforming to a specific C standard. Otherwise the comma is dropped as an extension to the standard.

So, (unless appropriate extension used) OUTPUT_ARGS_COUNT() is counted as 1 empty argument (comma kept with ##__VA_ARGS__).

2
votes

The C standard specifies

If the identifier-list in the macro definition does not end with an ellipsis, [...]. Otherwise, there shall be more arguments in the invocation than there are parameters in the macro definition (excluding the ...)

(C2011 6.10.3/4; emphasis added)

C++11 contains language to the same effect in paragraph 16.3/4.

In both cases, then, if your macro invocation were interpreted to have zero arguments then your program would be non-conforming. On the other hand, the preprocessor does recognize and support empty macro arguments -- that is, arguments consisting of zero preprocessing tokens. In principle, then, there is an ambiguity here between no argument and a single empty argument, but in practice, only the latter interpretation results in a conforming program.

That g++ opts for the latter interpretation (the other answer quotes its documentation to that effect) is thus reasonable and appropriate, but it is not safe to rely upon it if you want your code to be portable. A compiler that takes the alternative interpretation would behave differently, possibly by providing the behavior you expected, but also possibly by rejecting the code.