4

gcc does support argument counting macros with zero arguments with the ## __VA_ARGS__ convention. The following works compiled with gcc:

#include <stdio.h>

#define NARGS(...) __NARGS(0, ## __VA_ARGS__, 5,4,3,2,1,0)
#define __NARGS(_0,_1,_2,_3,_4,_5,N,...) N

int main()
{
  printf("%d\n", NARGS());     // prints 0
  printf("%d\n", NARGS(1));    // prints 1
  printf("%d\n", NARGS(1, 2)); // prints 2
  return 0;
}

Is there an equivalent for VisualC++ that will work with zero arguments macros? Non standard extensions or tricks accepted.

EDIT: Fixed the example to work with GCC extensions and C++ compiler.

8
  • 1
    Note that g++ (unlike gcc) prints also 1: (ideone.com/DTIkRF). Commented Oct 31, 2014 at 20:35
  • @Jarod42, weird. How do they count for macro arguments in c++ then? I already found a proper solution valid for VisualC++. Commented Nov 1, 2014 at 7:49
  • The so-called solution can be: stackoverflow.com/questions/2124339/… Commented Nov 3, 2014 at 11:28
  • Yeah, I found it yesterday as well. Nobody as yet properly argued against it. I'll try to understand the macro later. Commented Nov 3, 2014 at 19:05
  • Oh, you are also the reporter :) Commented Nov 3, 2014 at 20:01

4 Answers 4

16

The following example works fine in VisualStudio 2010 and newer, gcc and clang with non standard extensions enabled. In Microsoft compilers it assumes the trailing comma in the AUGMENTER macro will be removed by the preprocessor when arguments count is zero. This is non standard and it has been also reported elsewere. In gcc and clang it uses the widely known ## __VA_ARGS__ non standard extension.

#include <stdio.h>

#ifdef _MSC_VER // Microsoft compilers

#define EXPAND(x) x
#define __NARGS(_1, _2, _3, _4, _5, VAL, ...) VAL
#define NARGS_1(...) EXPAND(__NARGS(__VA_ARGS__, 4, 3, 2, 1, 0))

#define AUGMENTER(...) unused, __VA_ARGS__
#define NARGS(...) NARGS_1(AUGMENTER(__VA_ARGS__))

#else // Others

#define NARGS(...) __NARGS(0, ## __VA_ARGS__, 5,4,3,2,1,0)
#define __NARGS(_0,_1,_2,_3,_4,_5,N,...) N

#endif

int main()
{
  // NARGS
  printf("%d\n", NARGS());          // Prints 0
  printf("%d\n", NARGS(1));         // Prints 1
  printf("%d\n", NARGS(1, 2));      // Prints 2
  fflush(stdout);

#ifdef _MSC_VER
  // NARGS minus 1
  printf("\n");
  printf("%d\n", NARGS_1(1));       // Prints 0
  printf("%d\n", NARGS_1(1, 2));    // Prints 1
  printf("%d\n", NARGS_1(1, 2, 3)); // Prints 2
#endif

  return 0;
}

Macros were tested with real compilers, Wandbox and Webcompiler

Sign up to request clarification or add additional context in comments.

7 Comments

@BLUEPIXY: I also wrote the relevant macro that works with GCC C/C++ compilers with GNU extensions enabled.
Nice: someone downvoted and didn't write why. These macros costed hours of work so I would be pleased to know if they don't work for someone.
woaaa! EXPAND(x) x seems to be the one thing that I was missing to get my argument counter macro working in Visual Studio. I don't understand why this doesn't work for non-gnu gcc/clang. Either way, I'm upvoting this answer for the visual studio solution which saved me hours of research. Thank you!
EXPAND(x) x need is actually a bug in the VS2010 preprocessor that is fixed in the following releases.
@BLUEPIXY: did you notice the "with non standard extensions enabled"? Perhaps that also explains the downvote.
|
2

Here is some code that works with gcc (5.3) AND VisualStudio2010 :

#include <stdio.h>

#define expand(x)                          x
#define prefix(...)                        0,##__VA_ARGS__
#define lastof10(a,b,c,d,e,f,g,h,i,j,...)  j
#define sub_nbarg(...)                     expand(lastof10(__VA_ARGS__,8,7,6,5,4,3,2,1,0))
#define nbarg(...)                         sub_nbarg(prefix(__VA_ARGS__))

#define test(...) printf("(%s) ---> %d\n",""#__VA_ARGS__,nbarg(__VA_ARGS__))

int main () 
    {
    test() ;              // () ---> 0
    test(1) ;             // (1) ---> 1
    test(1,2) ;           // (1,2) ---> 2
    test(1,2,3) ;         // (1,2,3) ---> 3
    test(1,2,3,4) ;       // (1,2,3,4) ---> 4
    test(1,2,3,4,5) ;     // (1,2,3,4,5) ---> 5
    test(1,2,3,4,5,6) ;   // (1,2,3,4,5,6) ---> 6
    return 0 ;
    }

2 Comments

I had this problem solved long time ago with non stardard extensions but I was curious and tested your approach. I removed parenthesis from lastof10 to allow expansion after preprocessor to be exactly the number of args 0, 1, ..., and not (0), (1), ... The same example works in VS2010 and gcc. Does it work for you in your use cases? I should test your approach with my complex macro system to confirm your solution works for my use case (I'm using the number of args to create new macros), but thanks for investigating and proposing your solution.
Really not a problem for me, but even your approach is using non standard extensions: it will work with gcc with -std=gnu++11 or -std=gnu99 but not with -std=c++11 or -std=c99. The advantage of your approach is that it's portable on major compilers (*nix and Windows worlds).
0

Could you try:

#define __NARGS(_1, _2, _3, _4, _5, VAL, ...) VAL
#define NARGS(...) (sizeof(#__VA_ARGS__) == sizeof("") ? 0 : __NARGS(__VA_ARGS__, 5, 4, 3, 2, 1))

That works with g++ (Demo).

3 Comments

this is not work for VC++2010. sizeof(#__VA_ARGS__) exapand to sizeof() in VC, sizeof() is syntax error.
Also it does not solve the problems of the basic MSVC.
This is approach is limiting cause it requires code to be effectively compiled.
-1

Using the idea of @Jarod42 and BOOST_PP_VARIADIC_SIZE of boost can be written as follows.

#include <stdio.h>
#include <boost/preprocessor/variadic/size.hpp>

#define FOO(...) (sizeof(""#__VA_ARGS__) == sizeof("") ? 0 : BOOST_PP_VARIADIC_SIZE(__VA_ARGS__))

int main()
{
  printf("%d\n", FOO());     // prints 0
  printf("%d\n", FOO(1));    // prints 1
  printf("%d\n", FOO(1, 2)); // prints 2
  return 0;
}

4 Comments

This is quite useless as the replacement isn't an integer literal.
@Loopunroller What does that mean? Please indicate the bad concrete examples.
This is approach is also limiting requiring code to be compiled and depending on boost. Can you try the one in my answer and see it also works for you?
@ceztko You can be referred to the implementation of boost.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.