Xeo
Xeo

Reputation: 131857

Why does this variadic argument count macro fail with VC++?

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.

Upvotes: 18

Views: 4913

Answers (3)

Jacques
Jacques

Reputation: 319

This solution doesn't work with 0 arguments (because an empty __VA_ARGS__ is considered an empty argument, and not no argument :

#include <iostream>

   
using namespace std;

#define EXPAND(x) EXPAND_IMPL(EXPAND_IMPL(x))
#define EXPAND_IMPL(x) x

#define _xPP_NARGS_IMPL(x1,x2,x3,x4,x5,x6,x7,x8,x9,x10,x11,x12,x13,x14,x15,N,...) N
#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))

int main()
{
cout << PP_NARGS() << endl;
cout << PP_NARGS(a) << endl;
cout << PP_NARGS(a,b) << endl;

    return 0;
}

The output is:

1
1
2

And not:

0
1
2

Upvotes: 0

CygnusX1
CygnusX1

Reputation: 21818

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

Upvotes: 1

Ise Wisteria
Ise Wisteria

Reputation: 11669

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.

Upvotes: 25

Related Questions