18

I got the following implementation to get the number of arguments in a variadic macro (currently limited to 16 args). However, for VS2010 the output is always 1, no matter how many arguments are passed. With GCC, the output is correct, bringing me to the conclusion that I must have missed something specific for MSVC (10).

#define PP_NARGS(...) \
    _xPP_NARGS_IMPL(__VA_ARGS__,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)

#define _xPP_NARGS_IMPL(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,N,...) N

int main(){
    int i = PP_NARGS(A,V,C,X,Y,Z);

    std::cout << i;

    std::cin.get();
    return 0;
}

So, question is as the title states, any help would be appreciated.

a3f
  • 8,517
  • 1
  • 41
  • 46
Xeo
  • 129,499
  • 52
  • 291
  • 397

2 Answers2

25

Does the following work-around help?

#define EXPAND(x) x
#define PP_NARGS(...) \
    EXPAND(_xPP_NARGS_IMPL(__VA_ARGS__,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))

I think your macro isn't wrong in particular, but MSVC's __VA_ARGS__ expansion seems to behave differently from C99.

Ise Wisteria
  • 11,259
  • 2
  • 43
  • 26
  • Omg, it *does* work! Awesome! Do you have an explanation why though? – Xeo Apr 03 '11 at 17:30
  • 1
    I'm glad I could help :-) Honestly, I can't explain why(Doh). As I wrote in the answer, I think your macro is fine in C99 standard. I interpret the `EXPAND` stuff as a purely work-around, and probably there isn't any technically interesting matter in it... According to [here](http://connect.microsoft.com/VisualStudio/feedback/details/380090/variadic-macro-replacement), this seems like VC's bug-ish behaviour. – Ise Wisteria Apr 03 '11 at 18:36
  • Thank you very much for this workaround. Works as described in VS14 CTP3 (where a bug, acknowledged by MS in 2008 [cf. Ise's link], is still present). – Ad N Sep 15 '14 at 12:16
  • 2
    @IseWisteria @Xeo I was just hit with a similar problem (http://stackoverflow.com/questions/38209120/invalid-argument-count-in-a-variadic-macro) upon my investigation, I realized that `__VA_ARGS__` are expanded *after* they are passed into the macro. In your case, `x1` becomes `A,V,C,X,Y,Z` as a whole. The `EXPAND` trick forces it to be expanded earlier. – CygnusX1 Jul 05 '16 at 17:57
1

The problem seems that Visual Studio is expanding __VA_ARGS__ after passing it into the subsequent macro, while gcc expands it before passing.

In your case, PP_NARGS(A,V,C,X,Y,Z) binds A,V,C,X,Y,Z to __VA_ARGS__, and then passes it as a whole to _xPP_NARGS_IMPL.

As a test, run:

#define PP_NARGS(...) \
    _xPP_NARGS_IMPL(__VA_ARGS__,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0)

#define _xPP_NARGS_IMPL(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,N,...) \
  (std::cout << #x1 << std::endl, N) 

int main() {
  int i = PP_NARGS(A, V, C, X, Y, Z);
  std::cout << i;
  return 0;
}

You will see A, V, C, X, Y, Z printed on the screen, and not just A as you would probably expect.


A possible solution, as suggested by Ise Wisteria already, is to force the expansion via:

#define EXPAND(x) x
#define PP_NARGS(...) \
    EXPAND(_xPP_NARGS_IMPL(__VA_ARGS__,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))
CygnusX1
  • 20,968
  • 5
  • 65
  • 109