C++ preprocessor __VA_ARGS__ number of arguments
Asked Answered
C

13

131

Simple question for which I could not find answer on the net. In variadic argument macros, how to find the number of arguments? I am okay with boost preprocessor, if it has the solution.

If it makes a difference, I am trying to convert variable number of macro arguments to boost preprocessor sequence, list, or array for further reprocessing.

Cryo answered 23/1, 2010 at 19:11 Comment(5)
Just to be clear - you are asking about variadic macros, and not the macros used to create variadic C functions?Lumbye
are the arguments of the same type? if so, and if the type is known, there's a standard C solution via compound literals; if it's unknown, you could use __typeof__ to get it to work at least on some compilersArchil
Since the discussion is about the Boost preprocessor sequence etc, it has to be C++ (which is why I retagged the Q - but failed to change the question title)...Oops; I'll fix that.Cainozoic
@JonathanLeffler True, Boost is a C++ library. However, Boost.Preprocessor can be used with C. AFAIK, nothing it uses is C++ specific.Gatling
Related: #11762203Theatrics
O
114

This is actually compiler dependent, and not supported by any standard.

Here however you have a macro implementation that does the count:

#define PP_NARG(...) \
         PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

/* Some test cases */


PP_NARG(A) -> 1
PP_NARG(A,B) -> 2
PP_NARG(A,B,C) -> 3
PP_NARG(A,B,C,D) -> 4
PP_NARG(A,B,C,D,E) -> 5
PP_NARG(1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3) -> 63
Officious answered 23/1, 2010 at 19:22 Comment(15)
....but now is standard in C++0x and should've been longer ago because it allows a great way to guard varadic functions from corrupted calls (ie, you can pass values after the varadic items. This is actually a way of getting the count i used to use, but i guess sizeof could work too..Millepede
The answer links to another site. Also the link doesn't seem to point to the correct answer. And even if I managed to find the intended answer it does seem a poor one as it embeds an hardcoded "-1" that will be compiled. There are better methods.Moussorgsky
It doesn't seem to work in VS2013 for some reason, and I can't see why it doesn't work. Any idea?Forelimb
@Forelimb You may have a look at my complete answer here that comes with 2 versions that will respectively work with msvc and gcc/clang with extensions enabled. They can be easily ifdef-ed for wide compilers support.Moussorgsky
Thanks! this worked in Visual Studio 2013 for me: #define EXPAND(x) x #define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N #define PP_NARG(...) EXPAND(PP_ARG_N(__VA_ARGS__, 9,8,7,6,5,4,3,2,1,0)) ```Forelimb
PP_NARG() fails to return 0. The GET_ARG_COUNT() & Y_TUPLE_SIZE() solutions work.Mylander
"PP_NARG() fails to return 0" ...isn't necessarily a problem. One can say that PP_NARG() should return 1 for the same reason PP_NARG(,) should return 2. Detecting 0 may indeed be handy in some cases, but the solutions seem to either be less general (requiring that first token to be pasteable; which may or may not be okay depending on what you're using it for), or implementation specific (such as requiring gnu's comma-removing-paste trick).Manor
@HWalters no, there are solutions without the necessity to concatenate.Christcrossrow
Duplicate/related answers: 1) #11762203 and 2) (this one helps me see what's happening easier initially since it's a shorter macro): #11762203Theatrics
Why we cannot remove PP_NARG_? If I write #define PP_NARG(...) PP_ARG_N(__VA_ARGS__,PP_RSEQ_N()), I get errors.Azimuth
Fix it for empty argument list: If you can not use __VA_OPT__ (e.g. in VS2013), the following works for me: #define EXPAND(x) x #define SELECT_5(a,b,c,d,e,num,...) num #define ARGC_dummy(...) dummy,##__VA_ARGS__ #define ARGC_impl(...) EXPAND(SELECT_5(__VA_ARGS__,4,3,2,1,0)) #define ARGC(...) EXPAND(ARGC_impl(ARGC_dummy(__VA_ARGS__)))Psyche
Sorry, now I see, what I just wrote is almost the same as the answer of @user1187902 To make it work in VS2013 VC120, I only changed the 1st macro to #define COUNT_ARGS(...) EXPAND(COUNT_ARGS_(,##__VA_ARGS__,6,5,4,3,2,1,0))Psyche
love this solution because arguments to this macro can be not necessarily valid c syntax: auto blah = tensor_type<float, DIMS(batch size, num adj, 2(f0,f1))>(foo, bar); which makes code explain itself, instead of directly written numbers, and let others guess what are these dimensions for.Abstract
@Millepede could you please clarify how the macro is named in C++ standard? The PP_NARG with this exact name is certainly not in the standard.Disconcerted
For someone wanting to know how this works, section "COUNT_ARGUMENTS" in renenyffenegger.ch/notes/development/languages/C-C-plus-plus/… is helpful.Lied
L
124

I usually use this macro to find a number of params:

#define NUMARGS(...)  (sizeof((int[]){__VA_ARGS__})/sizeof(int))

Full example:

#include <stdio.h>
#include <string.h>
#include <stdarg.h>

#define NUMARGS(...)  (sizeof((int[]){__VA_ARGS__})/sizeof(int))
#define SUM(...)  (sum(NUMARGS(__VA_ARGS__), __VA_ARGS__))

void sum(int numargs, ...);

int main(int argc, char *argv[]) {

    SUM(1);
    SUM(1, 2);
    SUM(1, 2, 3);
    SUM(1, 2, 3, 4);

    return 1;
}

void sum(int numargs, ...) {
    int     total = 0;
    va_list ap;

    printf("sum() called with %d params:", numargs);
    va_start(ap, numargs);
    while (numargs--)
        total += va_arg(ap, int);
    va_end(ap);

    printf(" %d\n", total);

    return;
}

It is completely valid C99 code. It has one drawback, though - you cannot invoke the macro SUM() without params, but GCC has a solution to it - see here.

So in case of GCC you need to define macros like this:

#define       NUMARGS(...)  (sizeof((int[]){0, ##__VA_ARGS__})/sizeof(int)-1)
#define       SUM(...)  sum(NUMARGS(__VA_ARGS__), ##__VA_ARGS__)

and it will work even with empty parameter list

Lattice answered 23/1, 2010 at 19:41 Comment(32)
UM, it won't work for the OP, he needs the size for BOOST_PP which runs on compile time.Officious
Clever! Does it also work when sizeof(int) != sizeof(void *) ?Osteopathy
@Kornel Like any macro, it is evaluated at compile time. I have no idea about Boost, but anyway Boost isn't needed.Lattice
@Adam Because I cast {__VA_ARGS__} to int[], it is just int[], regardless of actual content of __VA_ARGS__Lattice
@qrdl, so I can write SUM( myfunc(2), myarr[10], 2+3 )?Officious
@Kornel Of course you can, and it works for both C99 and GCC. Why don't you just try it yourself?Lattice
It will (and did) crash at int main() { return NUMARGS("a",b,c); }Seriema
No, it won't crash - it just doesn't compile. C doesn't allow string literal in integer arrayLattice
In visual studio 2013 - "cast to incomplete array type "int []" is not allowed" I used Visual Studio C compiler.Please helpSequin
@BuddhikaChaturanga How do you call the macro?Lattice
I tested your example code nothing else.When I compiled it on GCC , code was compiled and ran perfectly,but in visual c/c++ compilers and intel c/c++ compiler(for windows) has given me that compile error.Sequin
@BuddhikaChaturanga Both gcc and clang with -std=c99 -Wpedantic compile it without any errors or warnings, and I believe these are two most standard-compliant compilers, so these should be something wrong with Microsoft's and Intel's compilersLattice
Yeah OpenSource-Free stuff are awesome rather than commercial stuff.. I cannot imagine why do they hesitate to maintain full standard ...Sequin
Umm, holy fking st, this one is smart enough and doesn't even need C++, it instead works as C just fine. I needed it for my personal projects so thanks! (whoops, formatting messed up my wording lol)Jarrad
Nice approach. Unfortunately it doesn't work when NUMARGS is used in #if :(Administer
@Administer What do you mean?Lattice
@Lattice , the code is simple: #if NUMARGS(10) > 0 causes the compile-time error with MSVC++ (VS2017) and gcc (Arduino 1.8.5).Administer
@Administer What are you trying to achieve? Looks like XY problem.Lattice
@qrdl, #define VALUE and then #if NUMARGS(VALUE) == 0 to show #error VALUE is not specifiedAdminister
@Administer I mean what's the point? Why just don't use #ifndef VALUE? Anyway sizeof is evaluated during the compile time, after the preprocessor, therefore result of sizeof is not available for the preprocessor.Lattice
@Lattice , I was going to detect two cases - one with #define VALUE, and another with #define VALUE 10. Apparently I need another solution for that...Administer
@Administer You can use #if VALUE == 10,` NUMARGS()` is for completely different purpose, it isn't a tool for your taskLattice
@qrdl, #if VALUE == 10 causes compile-time error when VALUE is defined without a value (i.e. #define VALUE)Administer
Elegant solution! Works in VS2017. The ## isn't needed in VS2017 as an empty __VA_ARGS__ will automatically remove any preceding comma.Stotts
Any reason for int vs char to drop the /sizeof(int)) and then have (sizeof((char[]){__VA_ARGS__}))?Ehrman
@chux Nope, should work the same. But probably some compilers will throw warnings about attempts to fit int into char. GCC is fine with char, even with -Wall -Wextra. It just didn't occur to me that it can be simplified. Thanks!Lattice
It's a great hack, indeed. The only thing I don't really like is that the whole argument list (##__VA_ARGS__) is evaluated twice, once for the "cast" and once for the actual argument passing. Nonetheless it's super cool.Fixative
This this only works when the arguments are integers? What if each argument has a different type, including structs?Flo
@étale-cohomology I think compiler will complain that it cannot cast struct to int. So it can work only with types that can be cast to intLattice
why not just cast to char[] instead, so you don't need to divideHartsock
Can you please update the answer to explain how this works and what exactly is happening in that NUMARGS macro? I hate using clever tricks that just look like magic to me. This answer is currently in that category.Theatrics
"It has one drawback, though - you cannot invoke the macro SUM() without params". Instead of gcc non-standard hacks, consider (sizeof(int[]){0,__VA_ARGS__} / sizeof(int) - 1).Fai
O
114

This is actually compiler dependent, and not supported by any standard.

Here however you have a macro implementation that does the count:

#define PP_NARG(...) \
         PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...) \
         PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
          _1, _2, _3, _4, _5, _6, _7, _8, _9,_10, \
         _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
         _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
         _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
         _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
         _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
         _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
         63,62,61,60,                   \
         59,58,57,56,55,54,53,52,51,50, \
         49,48,47,46,45,44,43,42,41,40, \
         39,38,37,36,35,34,33,32,31,30, \
         29,28,27,26,25,24,23,22,21,20, \
         19,18,17,16,15,14,13,12,11,10, \
         9,8,7,6,5,4,3,2,1,0

/* Some test cases */


PP_NARG(A) -> 1
PP_NARG(A,B) -> 2
PP_NARG(A,B,C) -> 3
PP_NARG(A,B,C,D) -> 4
PP_NARG(A,B,C,D,E) -> 5
PP_NARG(1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3,4,5,6,7,8,9,0,
         1,2,3) -> 63
Officious answered 23/1, 2010 at 19:22 Comment(15)
....but now is standard in C++0x and should've been longer ago because it allows a great way to guard varadic functions from corrupted calls (ie, you can pass values after the varadic items. This is actually a way of getting the count i used to use, but i guess sizeof could work too..Millepede
The answer links to another site. Also the link doesn't seem to point to the correct answer. And even if I managed to find the intended answer it does seem a poor one as it embeds an hardcoded "-1" that will be compiled. There are better methods.Moussorgsky
It doesn't seem to work in VS2013 for some reason, and I can't see why it doesn't work. Any idea?Forelimb
@Forelimb You may have a look at my complete answer here that comes with 2 versions that will respectively work with msvc and gcc/clang with extensions enabled. They can be easily ifdef-ed for wide compilers support.Moussorgsky
Thanks! this worked in Visual Studio 2013 for me: #define EXPAND(x) x #define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,N,...) N #define PP_NARG(...) EXPAND(PP_ARG_N(__VA_ARGS__, 9,8,7,6,5,4,3,2,1,0)) ```Forelimb
PP_NARG() fails to return 0. The GET_ARG_COUNT() & Y_TUPLE_SIZE() solutions work.Mylander
"PP_NARG() fails to return 0" ...isn't necessarily a problem. One can say that PP_NARG() should return 1 for the same reason PP_NARG(,) should return 2. Detecting 0 may indeed be handy in some cases, but the solutions seem to either be less general (requiring that first token to be pasteable; which may or may not be okay depending on what you're using it for), or implementation specific (such as requiring gnu's comma-removing-paste trick).Manor
@HWalters no, there are solutions without the necessity to concatenate.Christcrossrow
Duplicate/related answers: 1) #11762203 and 2) (this one helps me see what's happening easier initially since it's a shorter macro): #11762203Theatrics
Why we cannot remove PP_NARG_? If I write #define PP_NARG(...) PP_ARG_N(__VA_ARGS__,PP_RSEQ_N()), I get errors.Azimuth
Fix it for empty argument list: If you can not use __VA_OPT__ (e.g. in VS2013), the following works for me: #define EXPAND(x) x #define SELECT_5(a,b,c,d,e,num,...) num #define ARGC_dummy(...) dummy,##__VA_ARGS__ #define ARGC_impl(...) EXPAND(SELECT_5(__VA_ARGS__,4,3,2,1,0)) #define ARGC(...) EXPAND(ARGC_impl(ARGC_dummy(__VA_ARGS__)))Psyche
Sorry, now I see, what I just wrote is almost the same as the answer of @user1187902 To make it work in VS2013 VC120, I only changed the 1st macro to #define COUNT_ARGS(...) EXPAND(COUNT_ARGS_(,##__VA_ARGS__,6,5,4,3,2,1,0))Psyche
love this solution because arguments to this macro can be not necessarily valid c syntax: auto blah = tensor_type<float, DIMS(batch size, num adj, 2(f0,f1))>(foo, bar); which makes code explain itself, instead of directly written numbers, and let others guess what are these dimensions for.Abstract
@Millepede could you please clarify how the macro is named in C++ standard? The PP_NARG with this exact name is certainly not in the standard.Disconcerted
For someone wanting to know how this works, section "COUNT_ARGUMENTS" in renenyffenegger.ch/notes/development/languages/C-C-plus-plus/… is helpful.Lied
L
65

If you are using C++11, and you need the value as a C++ compile-time constant, a very elegant solution is this:

#include <tuple>

#define MACRO(...) \
    std::cout << "num args: " \
    << std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value \
    << std::endl;

Please note: the counting happens entirely at compile time, and the value can be used whenever compile-time integer is required, for instance as a template parameter to std::array.

Ludwig answered 26/10, 2015 at 15:8 Comment(7)
Great solution! And unlike sizeof((int[]){__VA_ARGS__})/sizeof(int) suggested above, it works even when the arguments cannot all be cast to int.Infringement
Agreed. Great solution! ++.Famine
Doesn't work with templates, i.e. NUMARGS( sum<1,2> ); see godbolt.org/z/_AAxmLFoil
I... think that might actually be a point in favour of it, @jorgbrown, at least in most cases where it'd come up. Since it relies on the compiler instead of the preprocessor to do the counting, it gives the number of arguments as seen by the compiler, which will likely match up with what most programmers expect. It will cause trouble if you expect it to take preprocessor greediness into account, though.Endodontics
Superb answer. You can put it into a macro #define NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::valueGuideboard
If using C++ then why not using the C++ way to do this? -- template<typename ...T> inline constexpr size_t number_of_args(T ... a) { return sizeof...(a); } -- that is standard C++ compile time solution. C++11 and beyond. Core language no std::.Bluebeard
Sadly using std functions or templates requires your arguments being counted to be valid (defined and well formed in syntax), the PP_NARG(...) solution works perfect when auto blah = tensor_type<float, PP_NARG(batch size, num adj, 2(f0,f1))>(foo, bar);Abstract
I
31

For convenience, here's an implementation that works for 0 to 70 arguments, and works in Visual Studio, GCC, and Clang. I believe it will work in Visual Studio 2010 and later, but have only tested it in VS2013-2022.

Update January 2023: I have tested and confirmed that the non-Microsoft variant will now work in Visual Studio 2019/2022 when using the /Zc:preprocessor compiler option, which provides a fully conformant C11 and C17 preprocessor. You can use the _MSVC_TRADITIONAL preprocessor symbol to test if this newer compliant preprocessor is being used (I have not updated the example below to do this, however).

#ifdef _MSC_VER // Microsoft compilers

#   define GET_ARG_COUNT(...)  INTERNAL_EXPAND_ARGS_PRIVATE(INTERNAL_ARGS_AUGMENTER(__VA_ARGS__))

#   define INTERNAL_ARGS_AUGMENTER(...) unused, __VA_ARGS__
#   define INTERNAL_EXPAND(x) x
#   define INTERNAL_EXPAND_ARGS_PRIVATE(...) INTERNAL_EXPAND(INTERNAL_GET_ARG_COUNT_PRIVATE(__VA_ARGS__, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#   define INTERNAL_GET_ARG_COUNT_PRIVATE(_1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, _33_, _34_, _35_, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, count, ...) count

#else // Non-Microsoft compilers

#   define GET_ARG_COUNT(...) INTERNAL_GET_ARG_COUNT_PRIVATE(0, ## __VA_ARGS__, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#   define INTERNAL_GET_ARG_COUNT_PRIVATE(_0, _1_, _2_, _3_, _4_, _5_, _6_, _7_, _8_, _9_, _10_, _11_, _12_, _13_, _14_, _15_, _16_, _17_, _18_, _19_, _20_, _21_, _22_, _23_, _24_, _25_, _26_, _27_, _28_, _29_, _30_, _31_, _32_, _33_, _34_, _35_, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, count, ...) count

#endif

static_assert(GET_ARG_COUNT() == 0, "GET_ARG_COUNT() failed for 0 arguments");
static_assert(GET_ARG_COUNT(1) == 1, "GET_ARG_COUNT() failed for 1 argument");
static_assert(GET_ARG_COUNT(1,2) == 2, "GET_ARG_COUNT() failed for 2 arguments");
static_assert(GET_ARG_COUNT(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70) == 70, "GET_ARG_COUNT() failed for 70 arguments");
Incursive answered 15/3, 2016 at 15:12 Comment(8)
IMHO the Microsoft variant fails for zero arguments.Christcrossrow
@Christcrossrow the Microsoft variant does work for zero arguments. The very first static_assert in example above is a specific test for the zero-argument case, and I just compiled and ran it on Visual Studio 2017 v15.8.9.Incursive
Interesting - using the the Microsoft variant on a non-Microsoft compiler does not work - do you know what the M$ preprocessor does differently that makes code work the opposite way? BTW I tried C, not C++;Christcrossrow
I believe that's because MSVC is a bit nicer about "zero-length __VA_ARGS__" (which in C++, is technically a (nigh-universal, de facto standard) compiler extension up until C++20). Most (all?) compilers allow zero-length, but choke on the trailing comma if the list is empty (and overload ## as a proto-__VA_OPT__, to remove the comma in this case); MSVC's version of the extension just doesn't choke on the comma (but will choke on the overloaded ##). Compare MSVC unused, __VA_ARGS__ to non-MSVC 0, ## __VA_ARGS__; neither is more correct, the problem is that they're different.Endodontics
I'm not sure if this is the same in C, though, @Vroomfondel, since I lost my bookmark to the most recent draft.Endodontics
Unfortunately gcc does not accept 0 arguments without the extension: at least -std=gnu++11 option must be set.Tuberculosis
The "Microsoft variant", I assume is whatever language cl.exe defines as C or C++, at some arbitrary moment in time. I prefer to follow the official WG14 defined standard, by avoiding cl.exe if at all possible.Bluebeard
Not working: GET_ARG_COUNT(ANY_MACRO(any_args...)) returns always 1Cyan
B
23

There are some C++11 solutions for finding the number of arguments at compile-time, but I'm surprised to see that no one has suggested anything so simple as:

#define VA_COUNT(...) detail::va_count(__VA_ARGS__)

namespace detail
{
    template<typename ...Args>
    constexpr std::size_t va_count(Args&&...) { return sizeof...(Args); }
}

This doesn't require inclusion of the <tuple> header either.

Beira answered 11/5, 2018 at 0:31 Comment(6)
"but why not just use a variadic template and sizeof... instead (as in my own answer)" c++ has become a monster. It has too many features and many of them, like variadic templates, are seldom used. You read about it, you write some examples and then you forget it. Therefore, it is hard to come up with the right idea at the right time. Since your solution seems to be a better option than mine, I will let the natural selection work and I will delete my solution.Kibbutz
@ZDF understandable, but I happen to use variadic templates constantly. My programs have become much more robust since C++11, and this is one of the main reasons why. No need to delete your answer though, I think.Beira
It won't work with smth like VA_COUNT(&,^,%). Also, if you are counting via a funtion, I don't see any sence in making a macro.Fissile
This solution remains a question: the parameters of VA_COUNT are all identifiers that not yet defined as a variable or something, and it causes error '*** variable is not defined'. Is there any way to fix this?Surfactant
In the context of standard C++ this is the right answer. Macro not needed. godbolt.org/z/varbM6r69Bluebeard
@ChefGladiator I included the macro in my answer merely to parallel the standard variadic argument macros, though like @Fissile points out it doesn't cover some more abstract use cases. Also "in the context of standard C++", you may want to use the C++ headers <cstdlib> and <cassert> rather than the C headers that pull things into the global namespace.Beira
O
9

this works with 0 arguments with gcc/llvm. [links are dumb]

/*
 * we need a comma at the start for ##_VA_ARGS__ to consume then
 * the arguments are pushed out in such a way that 'cnt' ends up with
 * the right count.  
 */
#define COUNT_ARGS(...) COUNT_ARGS_(,##__VA_ARGS__,6,5,4,3,2,1,0)
#define COUNT_ARGS_(z,a,b,c,d,e,f,cnt,...) cnt

#define C_ASSERT(test) \
    switch(0) {\
      case 0:\
      case test:;\
    }

int main() {
   C_ASSERT(0 ==  COUNT_ARGS());
   C_ASSERT(1 ==  COUNT_ARGS(a));
   C_ASSERT(2 ==  COUNT_ARGS(a,b));
   C_ASSERT(3 ==  COUNT_ARGS(a,b,c));
   C_ASSERT(4 ==  COUNT_ARGS(a,b,c,d));
   C_ASSERT(5 ==  COUNT_ARGS(a,b,c,d,e));
   C_ASSERT(6 ==  COUNT_ARGS(a,b,c,d,e,f));
   return 0;
}

Visual Studio seems to be ignoring the ## operator used to consume the empty argument. You can probably get around that with something like

#define CNT_ COUNT_ARGS
#define PASTE(x,y) PASTE_(x,y)
#define PASTE_(x,y) x ## y
#define CNT(...) PASTE(ARGVS,PASTE(CNT_(__VA_ARGS__),CNT_(1,##__VA_ARGS__)))
//you know its 0 if its 11 or 01
#define ARGVS11 0
#define ARGVS01 0
#define ARGVS12 1
#define ARGVS23 2
#define ARGVS34 3
Ornas answered 4/6, 2013 at 20:8 Comment(5)
I tested this for Visual Studio 2008 and it did not work for 0 arguments COUNT_ARGS() = 1.Lorislorita
The link seems broken.Ladoga
fixed link. VS must be doing something different as usual :). I don't think they are going to support C99 fully any time soon.Ornas
Er, ##__VA_ARGS__ eating the comma before if __VA_ARGS__ is empty is a GCC extension. It's not the standard behavior.Lumbye
It will not work with GCC if you use -std=c99.Hesitant
L
6

With msvc extension:

#define Y_TUPLE_SIZE(...) Y_TUPLE_SIZE_II((Y_TUPLE_SIZE_PREFIX_ ## __VA_ARGS__ ## _Y_TUPLE_SIZE_POSTFIX,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0))
#define Y_TUPLE_SIZE_II(__args) Y_TUPLE_SIZE_I __args

#define Y_TUPLE_SIZE_PREFIX__Y_TUPLE_SIZE_POSTFIX ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,0

#define Y_TUPLE_SIZE_I(__p0,__p1,__p2,__p3,__p4,__p5,__p6,__p7,__p8,__p9,__p10,__p11,__p12,__p13,__p14,__p15,__p16,__p17,__p18,__p19,__p20,__p21,__p22,__p23,__p24,__p25,__p26,__p27,__p28,__p29,__p30,__p31,__n,...) __n

Works for 0 - 32 arguments. This limit can be easily extended.

EDIT: Simplified version (works in VS2015 14.0.25431.01 Update 3 & gcc 7.4.0) up to 100 arguments to copy & paste:

#define COUNTOF(...) _COUNTOF_CAT( _COUNTOF_A, ( 0, ##__VA_ARGS__, 100,\
    99, 98, 97, 96, 95, 94, 93, 92, 91, 90,\
    89, 88, 87, 86, 85, 84, 83, 82, 81, 80,\
    79, 78, 77, 76, 75, 74, 73, 72, 71, 70,\
    69, 68, 67, 66, 65, 64, 63, 62, 61, 60,\
    59, 58, 57, 56, 55, 54, 53, 52, 51, 50,\
    49, 48, 47, 46, 45, 44, 43, 42, 41, 40,\
    39, 38, 37, 36, 35, 34, 33, 32, 31, 30,\
    29, 28, 27, 26, 25, 24, 23, 22, 21, 20,\
    19, 18, 17, 16, 15, 14, 13, 12, 11, 10,\
    9, 8, 7, 6, 5, 4, 3, 2, 1, 0 ) )
#define _COUNTOF_CAT( a, b ) a b
#define _COUNTOF_A( a0, a1, a2, a3, a4, a5, a6, a7, a8, a9,\
    a10, a11, a12, a13, a14, a15, a16, a17, a18, a19,\
    a20, a21, a22, a23, a24, a25, a26, a27, a28, a29,\
    a30, a31, a32, a33, a34, a35, a36, a37, a38, a39,\
    a40, a41, a42, a43, a44, a45, a46, a47, a48, a49,\
    a50, a51, a52, a53, a54, a55, a56, a57, a58, a59,\
    a60, a61, a62, a63, a64, a65, a66, a67, a68, a69,\
    a70, a71, a72, a73, a74, a75, a76, a77, a78, a79,\
    a80, a81, a82, a83, a84, a85, a86, a87, a88, a89,\
    a90, a91, a92, a93, a94, a95, a96, a97, a98, a99,\
    a100, n, ... ) n
Lorislorita answered 22/4, 2011 at 13:47 Comment(6)
is it just me or does this kinda break the code smell rules..?Millepede
It works for me with VC++ up to at least VS2012, and GCC and clang as well in my basic testing.Audley
@osirisgothra, exactly why it smells?Moussorgsky
While this macro has wide compilers support, it doesn't work with macro arguments such a string, like Y_TUPLE_SIZE("Hello"), making it quite infeasible. I agree with @osirisgothra.Moussorgsky
@Moussorgsky I've just tested it with VS2013 (Version 12.0.21005.1 REL) and that does work (However contrary to ThreeBit's statment it does not seem to work with GCC 4.8.3)Stenography
This macro can work for you but has serious defects. I did a lot of research and found cleaner approaches that work in GCC and VS. You can find them in my answer to a similar question.Moussorgsky
I
5

I'm assuming that each argument to __VA_ARGS__ will be comma separated. If so I think this should work as a pretty clean way to do this.

#include <cstring>

constexpr int CountOccurances(const char* str, char c) {
    return str[0] == char(0) ? 0 : (str[0] == c) + CountOccurances(str+1, c);
}

#define NUMARGS(...) (CountOccurances(#__VA_ARGS__, ',') + 1)

int main(){
    static_assert(NUMARGS(hello, world) == 2, ":(")  ;
    return 0;
}

Worked for me on godbolt for clang 4 and GCC 5.1. This will compute at compile time, but won't evaluate for the preprocessor. So if you are trying to do something like making a FOR_EACH, then this won't work.

Italia answered 24/7, 2018 at 16:51 Comment(3)
This answer is underrated. It'll work even for NUMARGS(hello, world = 2, ohmy42, !@#$%^&*()-+=) !!! Each arg string can't have some other symbols like ',' thoughWithy
Needs to be tweaked for parens, because int count = NUMARGS( foo(1, 2) ); produces 2 rather than 1. godbolt.org/z/kpBuOmFoil
This won't work as expected with lambdas, function calls or anything else that may contain extra commas in the parameters.Mama
F
4

herein a simple way to count 0 or more arguments of VA_ARGS, my example assumes a maximum of 5 variables, but you can add more if you want.

#define VA_ARGS_NUM_PRIV(P1, P2, P3, P4, P5, P6, Pn, ...) Pn
#define VA_ARGS_NUM(...) VA_ARGS_NUM_PRIV(-1, ##__VA_ARGS__, 5, 4, 3, 2, 1, 0)


VA_ARGS_NUM()      ==> 0
VA_ARGS_NUM(19)    ==> 1
VA_ARGS_NUM(9, 10) ==> 2
         ...
Forde answered 14/3, 2016 at 11:58 Comment(2)
Unfortunately the approach works incorrectly when VA_ARGS_NUM is used with macro: if I have #define TEST (i.e. empty TEST) and VA_ARGS_NUM(TEST) doesn't return 0 (zero) when used in #if :(Administer
@Administer can you post what you have done exactly please?Slipslop
T
3

I've found answers here still are incomplete.

The most closest portable implementation I've found from here is: C++ preprocessor __VA_ARGS__ number of arguments

But it doen't work with the zero arguments in the GCC without at least -std=gnu++11 command line parameter.

So I decided to merge this solution with that: https://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments/

#define UTILITY_PP_CONCAT_(v1, v2) v1 ## v2
#define UTILITY_PP_CONCAT(v1, v2) UTILITY_PP_CONCAT_(v1, v2)

#define UTILITY_PP_CONCAT5_(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4

#define UTILITY_PP_IDENTITY_(x) x
#define UTILITY_PP_IDENTITY(x) UTILITY_PP_IDENTITY_(x)

#define UTILITY_PP_VA_ARGS_(...) __VA_ARGS__
#define UTILITY_PP_VA_ARGS(...) UTILITY_PP_VA_ARGS_(__VA_ARGS__)

#define UTILITY_PP_IDENTITY_VA_ARGS_(x, ...) x, __VA_ARGS__
#define UTILITY_PP_IDENTITY_VA_ARGS(x, ...) UTILITY_PP_IDENTITY_VA_ARGS_(x, __VA_ARGS__)

#define UTILITY_PP_IIF_0(x, ...) __VA_ARGS__
#define UTILITY_PP_IIF_1(x, ...) x
#define UTILITY_PP_IIF(c) UTILITY_PP_CONCAT_(UTILITY_PP_IIF_, c)

#define UTILITY_PP_HAS_COMMA(...) UTILITY_PP_IDENTITY(UTILITY_PP_VA_ARGS_TAIL(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0))
#define UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_(...) ,

#define UTILITY_PP_IS_EMPTY(...) UTILITY_PP_IS_EMPTY_( \
    /* test if there is just one argument, eventually an empty one */ \
    UTILITY_PP_HAS_COMMA(__VA_ARGS__),                                \
    /* test if _TRIGGER_PARENTHESIS_ together with the argument adds a comma */ \
    UTILITY_PP_HAS_COMMA(UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_ __VA_ARGS__), \
    /* test if the argument together with a parenthesis adds a comma */ \
    UTILITY_PP_HAS_COMMA(__VA_ARGS__ ()),                             \
    /* test if placing it between _TRIGGER_PARENTHESIS_ and the parenthesis adds a comma */ \
    UTILITY_PP_HAS_COMMA(UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_ __VA_ARGS__ ()))

#define UTILITY_PP_IS_EMPTY_(_0, _1, _2, _3) UTILITY_PP_HAS_COMMA(UTILITY_PP_CONCAT5_(UTILITY_PP_IS_EMPTY_IS_EMPTY_CASE_, _0, _1, _2, _3))
#define UTILITY_PP_IS_EMPTY_IS_EMPTY_CASE_0001 ,

#define UTILITY_PP_VA_ARGS_SIZE(...) UTILITY_PP_IIF(UTILITY_PP_IS_EMPTY(__VA_ARGS__))(0, UTILITY_PP_VA_ARGS_SIZE_(__VA_ARGS__, UTILITY_PP_VA_ARGS_SEQ64()))
#define UTILITY_PP_VA_ARGS_SIZE_(...) UTILITY_PP_IDENTITY(UTILITY_PP_VA_ARGS_TAIL(__VA_ARGS__))

#define UTILITY_PP_VA_ARGS_TAIL(_0,_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14, x, ...) x
#define UTILITY_PP_VA_ARGS_SEQ64() 15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0

#define EATER0(...)
#define EATER1(...) ,
#define EATER2(...) (/*empty*/)
#define EATER3(...) (/*empty*/),
#define EATER4(...) EATER1
#define EATER5(...) EATER2
#define MAC0() ()
#define MAC1(x) ()
#define MACV(...) ()
#define MAC2(x,y) whatever

static_assert(UTILITY_PP_VA_ARGS_SIZE() == 0, "1");
static_assert(UTILITY_PP_VA_ARGS_SIZE(/*comment*/) == 0, "2");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a) == 1, "3");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b) == 2, "4");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b, c) == 3, "5");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b, c, d) == 4, "6");
static_assert(UTILITY_PP_VA_ARGS_SIZE(a, b, c, d, e) == 5, "7");
static_assert(UTILITY_PP_VA_ARGS_SIZE((void)) == 1, "8");
static_assert(UTILITY_PP_VA_ARGS_SIZE((void), b, c, d) == 4, "9");
static_assert(UTILITY_PP_VA_ARGS_SIZE(UTILITY_PP_IS_EMPTY_TRIGGER_PARENTHESIS_) == 1, "10");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER0) == 1, "11");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER1) == 1, "12");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER2) == 1, "13");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER3) == 1, "14");
static_assert(UTILITY_PP_VA_ARGS_SIZE(EATER4) == 1, "15");
static_assert(UTILITY_PP_VA_ARGS_SIZE(MAC0) == 1, "16");
// a warning in msvc
static_assert(UTILITY_PP_VA_ARGS_SIZE(MAC1) == 1, "17");
static_assert(UTILITY_PP_VA_ARGS_SIZE(MACV) == 1, "18");
// This one will fail because MAC2 is not called correctly
//static_assert(UTILITY_PP_VA_ARGS_SIZE(MAC2) == 1, "19");

https://godbolt.org/z/3idaKd

  • c++11, msvc 2015, gcc 4.7.1, clang 3.0
Tuberculosis answered 3/6, 2020 at 22:23 Comment(1)
Thanks for pointing out the gnu standard. It solved my problem :D, unlike other answers...Aristophanes
G
1

Boost Preprocessor actually has this as of Boost 1.49, as BOOST_PP_VARIADIC_SIZE(...). It works up to size 64.

Under the hood, it's basically the same as Kornel Kisielewicz's answer.

Gatling answered 20/12, 2018 at 16:27 Comment(1)
@CarloWood Indeed. The preprocessor doesn't really have the concept of "zero arguments". What we think of as "zero arguments" is "one empty argument" in the preprocessor. But it's fixable using C++20's __VA_OPT__ or the compiler extensions for ##__VA_ARGS__ removing the previous comma, e.g.: godbolt.org/z/X7OvnKGatling
D
0

You can stringfy and count tokens:

int countArgs(char *args)
{
    int result = 0;
    int i = 0;
         
    while (isspace(args[i])) 
        ++i;
    if (args[i]) 
        ++result;
     
    while (args[i++]) 
    {
        if (args[i] == ',') 
            ++result;
        else if (args[i] == '\'') 
            i += 2;
        else if (args[i] == '\"') 
            while (args[i++]) 
                if (args[i + 1] == '\"' && args[i] != '\\') 
                    break;
    }
 
    return result;
}

#define MACRO(...) \
{ \
    int count = countArgs(#__VA_ARGS__); \
    printf("NUM ARGS: %d\n",count); \
}
Degraded answered 20/3, 2015 at 12:15 Comment(1)
Just had a look at the edit pending on this answer - it appears you might have got two accounts. If you stick to one, you'll be able to edit your own posts without it going for approval.Subsoil
F
0

Edit: HolyBlackCat and Ted Lyngmo made this code a lot simpler

Try this

#include <iostream>

template<typename ...TArgs>
constexpr size_t GetArgsCount(TArgs&&...)
{
    return sizeof...(TArgs);
}

#define GET_ARGS_COUNT(...)\
    GetArgsCount(__VA_ARGS__)

int main()
{
    std::cout << GET_ARGS_COUNT() << '\n';                // 0
    std::cout << GET_ARGS_COUNT(false) << '\n';           // 1
    std::cout << GET_ARGS_COUNT(1, 2, 3) << '\n';         // 3
    std::cout << GET_ARGS_COUNT(1, "2", 3, 'c') << '\n';  // 4

    return 0;
}
Fug answered 16/2 at 6:34 Comment(3)
The second version can be greatly simplified to return (0 + ... + (void(args), 1)); or something similar.Threecolor
That is some beautiful code, thank you HolyBlackCatFug
What about return sizeof...(args);?Myopic

© 2022 - 2024 — McMap. All rights reserved.