C++ preprocessor __VA_ARGS__ number of arguments
Solution 1
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
Solution 2
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
Solution 3
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.
Solution 4
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.
#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");
Solution 5
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.

Anycorn
Andrey Asadchev in real life. Currently working in Silicon Valley. Previously was a PostDoc at VT (MPQC project), before that PhD at Iowa State. My interests are FISHING!!!, C++, Python, numerical algorithms, parallel and scientific programming, code optimization.
Updated on November 10, 2021Comments
-
Anycorn about 1 year
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.
-
Kornel Kisielewicz almost 13 yearsUM, it won't work for the OP, he needs the size for BOOST_PP which runs on compile time.
-
Adam Liss almost 13 yearsClever! Does it also work when
sizeof(int) != sizeof(void *)
? -
qrdl almost 13 years@Kornel Like any macro, it is evaluated at compile time. I have no idea about Boost, but anyway Boost isn't needed.
-
qrdl almost 13 years@Adam Because I cast
{__VA_ARGS__}
toint[]
, it is justint[]
, regardless of actual content of__VA_ARGS__
-
Kornel Kisielewicz almost 13 years@qrdl, so I can write
SUM( myfunc(2), myarr[10], 2+3 )
? -
qrdl almost 13 years@Kornel Of course you can, and it works for both C99 and GCC. Why don't you just try it yourself?
-
Frank Jüstel almost 10 yearsyou are just a genius :-) Thank's a lot. perfect!
-
osirisgothra almost 9 years....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 almost 9 yearsis it just me or does this kinda break the code smell rules..?
-
ThreeBit almost 9 yearsIt works for me with VC++ up to at least VS2012, and GCC and clang as well in my basic testing.
-
18446744073709551615 over 8 yearsIt will (and did) crash at
int main() { return NUMARGS("a",b,c); }
-
qrdl over 8 yearsNo, it won't crash - it just doesn't compile. C doesn't allow string literal in integer array
-
ceztko about 8 years@osirisgothra, exactly why it smells?
-
user720594 about 8 yearsI tested this for Visual Studio 2008 and it did not work for 0 arguments COUNT_ARGS() = 1.
-
ceztko about 8 yearsWhile 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 about 8 yearsThe 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.
-
user45891 almost 8 years@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)
-
ceztko almost 8 yearsThis 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.
-
J Richard Snape almost 8 yearsJust 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.
-
mchiasson over 7 yearsIt doesn't seem to work in VS2013 for some reason, and I can't see why it doesn't work. Any idea?
-
ceztko over 7 years@mchiasson 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.
-
mchiasson over 7 yearsThanks! 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))
``` -
Buddhika Chaturanga about 7 yearsIn visual studio 2013 - "cast to incomplete array type "int []" is not allowed" I used Visual Studio C compiler.Please help
-
qrdl about 7 years@BuddhikaChaturanga How do you call the macro?
-
Buddhika Chaturanga about 7 yearsI 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.
-
qrdl about 7 years@BuddhikaChaturanga Both
gcc
andclang
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 -
Buddhika Chaturanga about 7 yearsYeah OpenSource-Free stuff are awesome rather than commercial stuff.. I cannot imagine why do they hesitate to maintain full standard ...
-
Jan Smrčina almost 7 yearsThe link seems broken.
-
user1187902 almost 7 yearsfixed link. VS must be doing something different as usual :). I don't think they are going to support C99 fully any time soon.
-
Wim over 6 yearsGreat solution! And unlike
sizeof((int[]){__VA_ARGS__})/sizeof(int)
suggested above, it works even when the arguments cannot all be cast toint
. -
PSkocik over 6 years
PP_NARG()
fails to return 0. TheGET_ARG_COUNT()
&Y_TUPLE_SIZE()
solutions work. -
Paul Stelian about 6 yearsUmm, 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)
-
Nic over 5 yearsEr,
##__VA_ARGS__
eating the comma before if__VA_ARGS__
is empty is a GCC extension. It's not the standard behavior. -
H Walters over 5 years"
PP_NARG()
fails to return 0" ...isn't necessarily a problem. One can say thatPP_NARG()
should return 1 for the same reasonPP_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). -
AntonK almost 5 yearsNice approach. Unfortunately it doesn't work when
NUMARGS
is used in#if
:( -
AntonK almost 5 yearsUnfortunately the approach works incorrectly when
VA_ARGS_NUM
is used with macro: if I have#define TEST
(i.e. emptyTEST
) andVA_ARGS_NUM(TEST)
doesn't return 0 (zero) when used in#if
:( -
elhadi dp ıpɐɥןǝ almost 5 years@AntonK can you post what you have done exactly please?
-
qrdl almost 5 years@AntonK What do you mean?
-
AntonK almost 5 years@qrdl , the code is simple:
#if NUMARGS(10) > 0
causes the compile-time error with MSVC++ (VS2017) and gcc (Arduino 1.8.5). -
qrdl almost 5 years@AntonK What are you trying to achieve? Looks like XY problem.
-
AntonK almost 5 years@qrdl,
#define VALUE
and then#if NUMARGS(VALUE) == 0
to show#error VALUE is not specified
-
qrdl almost 5 years@AntonK I mean what's the point? Why just don't use
#ifndef VALUE
? Anywaysizeof
is evaluated during the compile time, after the preprocessor, therefore result ofsizeof
is not available for the preprocessor. -
AntonK almost 5 years@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... -
qrdl almost 5 years@AntonK You can use
#if VALUE == 10
,` NUMARGS()` is for completely different purpose, it isn't a tool for your task -
AntonK over 4 years@qrdl,
#if VALUE == 10
causes compile-time error whenVALUE
is defined without a value (i.e.#define VALUE
) -
zdf over 4 years"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.
-
monkey0506 over 4 years@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.
-
Qwertiy over 4 yearsIt 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. -
poby about 4 yearsElegant solution! Works in VS2017. The
##
isn't needed in VS2017 as an empty__VA_ARGS__
will automatically remove any preceding comma. -
Vroomfondel about 4 years@HWalters no, there are solutions without the necessity to concatenate.
-
Vroomfondel about 4 yearsIMHO the Microsoft variant fails for zero arguments.
-
Chris Kline about 4 years@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.
-
Vroomfondel about 4 yearsInteresting - 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++;
-
pterodragon about 4 yearsThis answer is underrated. It'll work even for
NUMARGS(hello, world = 2, ohmy42, [email protected]#$%^&*()-+=)
!!! Each arg string can't have some other symbols like','
though -
chux - Reinstate Monica almost 4 yearsAny reason for
int
vschar
to drop the/sizeof(int))
and then have(sizeof((char[]){__VA_ARGS__}))
? -
qrdl almost 4 years@chux Nope, should work the same. But probably some compilers will throw warnings about attempts to fit
int
intochar
. GCC is fine withchar
, even with-Wall -Wextra
. It just didn't occur to me that it can be simplified. Thanks! -
ipid over 3 yearsThis 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?
-
Justin over 3 years@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/X7OvnK -
davernator over 3 yearsAgreed. Great solution! ++.
-
jorgbrown about 3 yearsNeeds to be tweaked for parens, because
int count = NUMARGS( foo(1, 2) );
produces 2 rather than 1. godbolt.org/z/kpBuOm -
jorgbrown about 3 yearsDoesn't work with templates, i.e. NUMARGS( sum<1,2> ); see godbolt.org/z/_AAxmL
-
Justin Time - Reinstate Monica about 3 yearsI... 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 about 3 yearsI 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 MSVCunused, __VA_ARGS__
to non-MSVC0, ## __VA_ARGS__
; neither is more correct, the problem is that they're different. -
Justin Time - Reinstate Monica about 3 yearsI'm not sure if this is the same in C, though, @Vroomfondel, since I lost my bookmark to the most recent draft.
-
Nandee about 3 yearsThis won't work as expected with lambdas, function calls or anything else that may contain extra commas in the parameters.
-
Gabriel Staples almost 3 yearsDuplicate/related answers: 1) stackoverflow.com/questions/11761703/… and 2) (this one helps me see what's happening easier initially since it's a shorter macro): stackoverflow.com/questions/11761703/…
-
Andry over 2 yearsUnfortunately gcc does not accept 0 arguments without the extension: at least
-std=gnu++11
option must be set. -
Richard Whitehead over 2 yearsSuperb answer. You can put it into a macro
#define NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value
-
Berthin about 2 yearsThanks for pointing out the gnu standard. It solved my problem :D, unlike other answers...
-
EnzoR over 1 yearIt'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.
-
Tokubara over 1 yearWhy we cannot remove
PP_NARG_
? If I write#define PP_NARG(...) PP_ARG_N(__VA_ARGS__,PP_RSEQ_N())
, I get errors. -
étale-cohomology over 1 yearThis this only works when the arguments are integers? What if each argument has a different type, including structs?
-
qrdl over 1 year@étale-cohomology I think compiler will complain that it cannot cast
struct
toint
. So it can work only with types that can be cast toint
-
Kaz about 1 yearIt will not work with GCC if you use
-std=c99
. -
Superior 11 monthswhy not just cast to
char[]
instead, so you don't need to divide -
Gabriel Staples 11 monthsCan 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. -
Chef Gladiator 10 monthsIf 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 nostd::
. -
Chef Gladiator 10 monthsIn the context of standard
C++
this is the right answer. Macro not needed. godbolt.org/z/varbM6r69 -
monkey0506 10 months@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 theC++
headers<cstdlib>
and<cassert>
rather than theC
headers that pull things into the global namespace. -
Toni Schilling 8 monthsFix 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 8 monthsSorry, 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))
-
imkzh 6 monthslove 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. -
imkzh 6 monthsSadly 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 whenauto blah = tensor_type<float, PP_NARG(batch size, num adj, 2(f0,f1))>(foo, bar);