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.

ceztko
  • 14,736
  • 5
  • 58
  • 73
  • 1
    Note that g++ (unlike gcc) prints also 1: (https://ideone.com/DTIkRF). – Jarod42 Oct 31 '14 at 20:35
  • @Jarod42, weird. How do they count for macro arguments in c++ then? I already found a proper solution valid for VisualC++. – ceztko Nov 01 '14 at 07:49
  • The so-called solution can be: http://stackoverflow.com/questions/2124339/c-preprocessor-va-args-number-of-arguments/5756072#5756072 – user720594 Nov 03 '14 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. – ceztko Nov 03 '14 at 19:05
  • Oh, you are also the reporter :) – ceztko Nov 03 '14 at 20:01
  • @user720594: I tried your macro. It works in both GCC and MSVC but with the latter it fails used in more advanced scenarios. – ceztko Dec 14 '14 at 21:33
  • @Jarod32: now the example also works with GCC. Together with the solution I found for MSVC the issue is closed for me. – ceztko Dec 14 '14 at 23:45
  • related question: [Macro to count number of arguments](http://stackoverflow.com/questions/11317474) – Eitan T Dec 07 '15 at 09:56

4 Answers4

14

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

ceztko
  • 14,736
  • 5
  • 58
  • 73
  • @BLUEPIXY: I also wrote the relevant macro that works with GCC C/C++ compilers with GNU extensions enabled. – ceztko Dec 15 '14 at 11:17
  • 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. – ceztko Apr 25 '15 at 15:35
  • 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! – mchiasson Jun 04 '15 at 14:51
  • `EXPAND(x) x` need is actually a bug in the VS2010 preprocessor that is fixed in the following releases. – ceztko Jun 05 '15 at 21:45
  • @BLUEPIXY: did you notice the "with non standard extensions enabled"? Perhaps that also explains the downvote. – Technophile Jul 28 '16 at 22:39
  • @Technophile As far as I know, there's no clean way to achieve this without non standard extensions. Other solutions I found are very dirty tricks that will just stress the preprocessor and fail in some useful use cases. Proposals were made to add `## __VA_ARGS__` to the standard, but failed for some reasons. Until this is addressed in the standard all existing solutions are lacking in some way. – ceztko Jul 29 '16 at 09:35
  • Could you link the actual tries (rather than the front pages) on Wandbox and Webcompiler? – Bulletmagnet Dec 06 '16 at 10:32
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 ;
    }
ceztko
  • 14,736
  • 5
  • 58
  • 73
Captain'Flam
  • 479
  • 4
  • 12
  • 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. – ceztko Jun 01 '17 at 12:15
  • 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). – ceztko Jun 01 '17 at 12:24
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).

Jarod42
  • 203,559
  • 14
  • 181
  • 302
-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;
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70