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 yearSimple 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 whensizeof(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 writeSUM( 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 atint 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, likeY_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 Bothgccandclangwith-std=c99 -Wpedanticcompile 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 unlikesizeof((int[]){__VA_ARGS__})/sizeof(int)suggested above, it works even when the arguments cannot all be cast toint. -
PSkocik over 6 yearsPP_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 whenNUMARGSis used in#if:( -
AntonK almost 5 yearsUnfortunately the approach works incorrectly whenVA_ARGS_NUMis 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) > 0causes 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 VALUEand then#if NUMARGS(VALUE) == 0to show#error VALUE is not specified -
qrdl almost 5 years@AntonK I mean what's the point? Why just don't use#ifndef VALUE? Anywaysizeofis evaluated during the compile time, after the preprocessor, therefore result ofsizeofis 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 == 10causes compile-time error whenVALUEis 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 likeVA_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 forNUMARGS(hello, world = 2, ohmy42, [email protected]#$%^&*()-+=)!!! Each arg string can't have some other symbols like','though -
chux - Reinstate Monica almost 4 yearsAny reason forintvscharto 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 fitintintochar. 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, becauseint 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++11option 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 removePP_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 caststructtoint. 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 tochar[]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 thatNUMARGSmacro? 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 standardC++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 standardC++", you may want to use theC++headers<cstdlib>and<cassert>rather than theCheaders 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), thePP_NARG(...)solution works perfect whenauto blah = tensor_type<float, PP_NARG(batch size, num adj, 2(f0,f1))>(foo, bar);