2

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.

Brian
  • 3,264
  • 4
  • 30
  • 43
  • 1
    You can see at http://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 Oct 17 '17 at 15:48
  • Check this https://stackoverflow.com/questions/2308243/macro-returning-the-number-of-arguments-it-is-given-in-c, the solution there also fails on zero arguments – Passer By Oct 17 '17 at 16:00

2 Answers2

3

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

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • I saw that documentation after your comment to my original post, but I don't know what to change based on it to fix it so I can get 0 when I call OUTPUT_ARGS_COUNT(). Are you suggesting it's not possible? Or perhaps I'm just not understanding what the appropriate extension is you are referring to. – Brian Oct 17 '17 at 15:56
  • 1
    As you can see in that [Demo](http://coliru.stacked-crooked.com/a/090b4438717735c3), `-std=gnu++XX` gives `0` whereas `-std=c++XX` gives `1`. You even got explicit warning about that construct. – Jarod42 Oct 17 '17 at 16:08
  • 1
    @Brian: as stated in the quote, it depends on what std version you're compiling with. If you compile with eg, `-std=c99`, you'll get the output you show. On the other hand, if you use `-std=gnu99`, you'll get `2 1 0`. – Chris Dodd Oct 17 '17 at 16:08
  • I'm compiling with -std=c++11, so I'm pretty sure this is what is getting me. I'm afraid to switch to gnu11 based on fact a lot of testing has already been performed and I don't wish to invalidate any of it. Hmmmmmm. . . decisions decisions! – Brian Oct 17 '17 at 16:18
  • 1
    @Brian If you are working with values as in the stuff shown above, just go with a variadic template and save yourself the avalanche of headaches – Passer By Oct 17 '17 at 16:22
  • @PasserBy That was the key. I still have my macro doing my macro thing, but I wrapped inside the macro the function call I really wanted to make and now I'm in business. Thanks so much for pointing me in the right direction – Brian Oct 17 '17 at 19:14
2

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157