130

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.

Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
Anycorn
  • 50,217
  • 42
  • 167
  • 261
  • Just to be clear - you are asking about variadic macros, and not the macros used to create variadic C functions? –  Jan 23 '10 at 19:23
  • 2
    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 compilers – Christoph Jan 23 '10 at 19:24
  • 1
    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. – Jonathan Leffler Jan 23 '10 at 19:25
  • @JonathanLeffler True, Boost is a C++ library. However, Boost.Preprocessor can be used with C. AFAIK, nothing it uses is C++ specific. – Justin Dec 20 '18 at 16:13
  • Related: https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments/26408195 – Gabriel Staples Mar 05 '20 at 20:41

12 Answers12

121

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

qrdl
  • 34,062
  • 14
  • 56
  • 86
  • 4
    UM, it won't work for the OP, he needs the size for BOOST_PP which runs on compile time. – Kornel Kisielewicz Jan 23 '10 at 19:50
  • 5
    Clever! Does it also work when `sizeof(int) != sizeof(void *)` ? – Adam Liss Jan 23 '10 at 19:52
  • 3
    @Kornel Like any macro, it is evaluated at compile time. I have no idea about Boost, but anyway Boost isn't needed. – qrdl Jan 23 '10 at 20:44
  • 4
    @Adam Because I cast `{__VA_ARGS__}` to `int[]`, it is just `int[]`, regardless of actual content of `__VA_ARGS__` – qrdl Jan 23 '10 at 20:45
  • @qrdl, so I can write `SUM( myfunc(2), myarr[10], 2+3 )`? – Kornel Kisielewicz Jan 23 '10 at 21:34
  • @Kornel Of course you can, and it works for both C99 and GCC. Why don't you just try it yourself? – qrdl Jan 24 '10 at 07:09
  • you are just a genius :-) Thank's a lot. perfect! – Frank Jüstel Jan 29 '13 at 19:41
  • It will (and did) crash at `int main() { return NUMARGS("a",b,c); }` – 18446744073709551615 May 15 '14 at 08:04
  • 2
    No, it won't crash - it just doesn't compile. C doesn't allow string literal in integer array – qrdl Sep 24 '14 at 08:21
  • In visual studio 2013 - "cast to incomplete array type "int []" is not allowed" I used Visual Studio C compiler.Please help – Buddhika Chaturanga Dec 18 '15 at 09:59
  • @BuddhikaChaturanga How do you call the macro? – qrdl Dec 18 '15 at 15:54
  • 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. – Buddhika Chaturanga Dec 20 '15 at 02:40
  • @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 compilers – qrdl Dec 20 '15 at 07:34
  • Yeah OpenSource-Free stuff are awesome rather than commercial stuff.. I cannot imagine why do they hesitate to maintain full standard ... – Buddhika Chaturanga Dec 21 '15 at 04:13
  • Umm, holy f**king s**t, 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) – Paul Stelian Dec 21 '16 at 21:49
  • Nice approach. Unfortunately it doesn't work when `NUMARGS` is used in `#if` :( – AntonK Mar 15 '18 at 11:31
  • @AntonK What do you mean? – qrdl Mar 16 '18 at 05:28
  • @qrdl , the code is simple: `#if NUMARGS(10) > 0` causes the compile-time error with MSVC++ (VS2017) and gcc (Arduino 1.8.5). – AntonK Mar 27 '18 at 12:22
  • @AntonK What are you trying to achieve? Looks like XY problem. – qrdl Mar 27 '18 at 12:31
  • @qrdl, `#define VALUE` and then `#if NUMARGS(VALUE) == 0` to show `#error VALUE is not specified` – AntonK Mar 27 '18 at 12:35
  • @AntonK 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. – qrdl Mar 27 '18 at 13:01
  • @qrdl , I was going to detect two cases - one with `#define VALUE`, and another with `#define VALUE 10`. Apparently I need another solution for that... – AntonK Mar 27 '18 at 13:57
  • @AntonK You can use `#if VALUE == 10`,` NUMARGS()` is for completely different purpose, it isn't a tool for your task – qrdl Mar 27 '18 at 14:26
  • @qrdl, `#if VALUE == 10` causes compile-time error when `VALUE` is defined without a value (i.e. `#define VALUE`) – AntonK Apr 03 '18 at 09:35
  • 3
    Elegant solution! Works in VS2017. The `##` isn't needed in VS2017 as an empty `__VA_ARGS__` will automatically remove any preceding comma. – poby Oct 07 '18 at 02:24
  • 1
    Any reason for `int` vs `char` to drop the `/sizeof(int))` and then have `(sizeof((char[]){__VA_ARGS__}))`? – chux - Reinstate Monica Jan 03 '19 at 15:19
  • 1
    @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! – qrdl Jan 03 '19 at 17:31
  • 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. – EnzoR Apr 27 '21 at 11:41
  • 1
    This this only works when the arguments are integers? What if each argument has a different type, including structs? – étale-cohomology Jul 07 '21 at 12:52
  • @é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 `int` – qrdl Jul 08 '21 at 13:06
  • why not just cast to `char[]` instead, so you don't need to divide – Superior Jan 26 '22 at 22:11
  • 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. – Gabriel Staples Feb 13 '22 at 04:21
  • "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)`. – Lundin Oct 03 '22 at 09:53
108

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
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
Kornel Kisielewicz
  • 55,802
  • 15
  • 111
  • 149
  • ....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.. – osirisgothra Jan 04 '14 at 09:17
  • 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. – ceztko Dec 18 '14 at 00:03
  • It doesn't seem to work in VS2013 for some reason, and I can't see why it doesn't work. Any idea? – mchiasson Jun 04 '15 at 14:13
  • @mchiasson You may have a look at my complete answer [here](http://stackoverflow.com/a/26685339/213871) 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. – ceztko Jun 04 '15 at 14:18
  • 4
    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))` ``` – mchiasson Jun 04 '15 at 15:06
  • 2
    `PP_NARG()` fails to return 0. The `GET_ARG_COUNT()` & `Y_TUPLE_SIZE()` solutions work. – Petr Skocik Jul 26 '16 at 11:13
  • 1
    "`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). – H Walters Jun 16 '17 at 05:13
  • @HWalters no, there are solutions without the necessity to concatenate. – Vroomfondel Nov 08 '18 at 21:27
  • 2
    Duplicate/related answers: 1) https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments/26408195#26408195 and 2) (this one helps me see what's happening easier initially since it's a shorter macro): https://stackoverflow.com/questions/11761703/overloading-macro-on-number-of-arguments/11763277#11763277 – Gabriel Staples Mar 05 '20 at 20:52
  • Why we cannot remove `PP_NARG_`? If I write `#define PP_NARG(...) PP_ARG_N(__VA_ARGS__,PP_RSEQ_N())`, I get errors. – Tokubara May 10 '21 at 03:15
  • 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__)))` – Toni Schilling May 01 '22 at 12:53
  • 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))` – Toni Schilling May 01 '22 at 14:42
  • love this solution because arguments to this macro can be not necessarily valid c syntax: `auto blah = tensor_type(foo, bar);` which makes code explain itself, instead of directly written numbers, and let others guess what are these dimensions for. – imkzh Jul 01 '22 at 03:02
  • @osirisgothra 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. – Hi-Angel Aug 23 '23 at 09:46
61

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.

Wim
  • 11,091
  • 41
  • 58
maciek gajewski
  • 903
  • 7
  • 5
  • 2
    Great solution! And unlike `sizeof((int[]){__VA_ARGS__})/sizeof(int)` suggested above, it works even when the arguments cannot all be cast to `int`. – Wim Jun 21 '16 at 09:14
  • Agreed. Great solution! ++. – davernator Aug 03 '19 at 20:55
  • 2
    Doesn't work with templates, i.e. NUMARGS( sum<1,2> ); see https://godbolt.org/z/_AAxmL – jorgbrown Oct 09 '19 at 22:37
  • 1
    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. – Justin Time - Reinstate Monica Oct 29 '19 at 18:30
  • Superb answer. You can put it into a macro `#define NUM_ARGS(...) std::tuple_size::value` – Richard Whitehead Jun 18 '20 at 10:05
  • 1
    If using C++ then why not using the C++ way to do this? -- `template 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::`. – Chef Gladiator Feb 27 '22 at 10:21
  • 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(foo, bar);` – imkzh Jul 01 '22 at 03:07
30

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");
Chris Kline
  • 2,249
  • 26
  • 32
  • IMHO the Microsoft variant fails for zero arguments. – Vroomfondel Nov 08 '18 at 21:37
  • 2
    @Vroomfondel 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. – Chris Kline Nov 09 '18 at 13:13
  • 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++; – Vroomfondel Nov 09 '18 at 14:52
  • 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. – Justin Time - Reinstate Monica Oct 29 '19 at 21:33
  • I'm not sure if this is the same in C, though, @Vroomfondel, since I lost my bookmark to the most recent draft. – Justin Time - Reinstate Monica Oct 29 '19 at 21:34
  • 2
    Unfortunately gcc does not accept 0 arguments without the extension: at least `-std=gnu++11` option must be set. – Andry Jun 03 '20 at 18:01
  • 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. – Chef Gladiator Jul 16 '22 at 09:04
  • Not working: GET_ARG_COUNT(ANY_MACRO(any_args...)) returns always 1 – jenkas Oct 29 '22 at 23:22
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.

monkey0506
  • 2,489
  • 1
  • 21
  • 27
  • 3
    _"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. – zdf May 11 '18 at 10:36
  • 3
    @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. – monkey0506 May 11 '18 at 14:01
  • 3
    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. – Qwertiy Aug 15 '18 at 23:39
  • 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? – ipid May 18 '19 at 10:08
  • In the context of standard `C++` this is the right answer. Macro not needed. https://godbolt.org/z/varbM6r69 – Chef Gladiator Feb 27 '22 at 10:30
  • 1
    @ChefGladiator I included the macro in my answer merely to parallel the standard variadic argument macros, though like @Qwertiy 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 `` and `` rather than the `C` headers that pull things into the global namespace. – monkey0506 Feb 28 '22 at 11:56
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
user1187902
  • 169
  • 1
  • 3
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
user720594
  • 1,022
  • 8
  • 8
  • 4
    is it just me or does this kinda break the code smell rules..? – osirisgothra Jan 04 '14 at 09:19
  • It works for me with VC++ up to at least VS2012, and GCC and clang as well in my basic testing. – ThreeBit Mar 23 '14 at 18:53
  • @osirisgothra, exactly why it smells? – ceztko Nov 02 '14 at 22:59
  • 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. – ceztko Dec 15 '14 at 11:23
  • @ceztko 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) – user45891 Feb 28 '15 at 00:45
  • 1
    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](http://stackoverflow.com/a/26685339/213871) to a similar question. – ceztko Feb 28 '15 at 01:01
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.

ndim
  • 35,870
  • 12
  • 47
  • 57
matanmarkind
  • 219
  • 3
  • 13
  • 1
    This answer is underrated. It'll work even for `NUMARGS(hello, world = 2, ohmy42, !@#$%^&*()-+=)` !!! Each arg string can't have some other symbols like `','` though – pterodragon Nov 30 '18 at 05:11
  • Needs to be tweaked for parens, because `int count = NUMARGS( foo(1, 2) );` produces 2 rather than 1. https://godbolt.org/z/kpBuOm – jorgbrown Oct 09 '19 at 22:24
  • This won't work as expected with lambdas, function calls or anything else that may contain extra commas in the parameters. – Nandee Oct 31 '19 at 20:52
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
         ...
elhadi dp ıpɐɥןǝ
  • 4,763
  • 2
  • 30
  • 34
  • 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` :( – AntonK Mar 15 '18 at 11:39
  • @AntonK can you post what you have done exactly please? – elhadi dp ıpɐɥןǝ Mar 15 '18 at 15:51
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
Andry
  • 2,273
  • 29
  • 28
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.

Justin
  • 24,288
  • 12
  • 92
  • 142
  • @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.: https://godbolt.org/z/X7OvnK – Justin Jun 20 '19 at 18:28
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); \
}
AustinWBryan
  • 3,249
  • 3
  • 24
  • 42
  • 2
    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. – J Richard Snape Mar 20 '15 at 16:51