C++ preprocessor __VA_ARGS__ number of arguments

145,176

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.

Share:
145,176
Anycorn
Author by

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, 2021

Comments

  • Anycorn
    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
    Kornel Kisielewicz almost 13 years
    UM, it won't work for the OP, he needs the size for BOOST_PP which runs on compile time.
  • Adam Liss
    Adam Liss almost 13 years
    Clever! Does it also work when sizeof(int) != sizeof(void *) ?
  • qrdl
    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
    qrdl almost 13 years
    @Adam Because I cast {__VA_ARGS__} to int[], it is just int[], regardless of actual content of __VA_ARGS__
  • Kornel Kisielewicz
    Kornel Kisielewicz almost 13 years
    @qrdl, so I can write SUM( myfunc(2), myarr[10], 2+3 )?
  • qrdl
    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
    Frank Jüstel almost 10 years
    you are just a genius :-) Thank's a lot. perfect!
  • osirisgothra
    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
    osirisgothra almost 9 years
    is it just me or does this kinda break the code smell rules..?
  • ThreeBit
    ThreeBit almost 9 years
    It works for me with VC++ up to at least VS2012, and GCC and clang as well in my basic testing.
  • 18446744073709551615
    18446744073709551615 over 8 years
    It will (and did) crash at int main() { return NUMARGS("a",b,c); }
  • qrdl
    qrdl over 8 years
    No, it won't crash - it just doesn't compile. C doesn't allow string literal in integer array
  • ceztko
    ceztko about 8 years
    @osirisgothra, exactly why it smells?
  • user720594
    user720594 about 8 years
    I tested this for Visual Studio 2008 and it did not work for 0 arguments COUNT_ARGS() = 1.
  • ceztko
    ceztko about 8 years
    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
    ceztko about 8 years
    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.
  • user45891
    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
    ceztko almost 8 years
    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.
  • J Richard Snape
    J Richard Snape almost 8 years
    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.
  • mchiasson
    mchiasson over 7 years
    It doesn't seem to work in VS2013 for some reason, and I can't see why it doesn't work. Any idea?
  • ceztko
    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
    mchiasson over 7 years
    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)) ```
  • Buddhika Chaturanga
    Buddhika Chaturanga about 7 years
    In visual studio 2013 - "cast to incomplete array type "int []" is not allowed" I used Visual Studio C compiler.Please help
  • qrdl
    qrdl about 7 years
    @BuddhikaChaturanga How do you call the macro?
  • Buddhika Chaturanga
    Buddhika Chaturanga about 7 years
    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.
  • qrdl
    qrdl about 7 years
    @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
  • Buddhika Chaturanga
    Buddhika Chaturanga about 7 years
    Yeah OpenSource-Free stuff are awesome rather than commercial stuff.. I cannot imagine why do they hesitate to maintain full standard ...
  • Jan Smrčina
    Jan Smrčina almost 7 years
    The link seems broken.
  • user1187902
    user1187902 almost 7 years
    fixed link. VS must be doing something different as usual :). I don't think they are going to support C99 fully any time soon.
  • Wim
    Wim over 6 years
    Great solution! And unlike sizeof((int[]){__VA_ARGS__})/sizeof(int) suggested above, it works even when the arguments cannot all be cast to int.
  • PSkocik
    PSkocik over 6 years
    PP_NARG() fails to return 0. The GET_ARG_COUNT() & Y_TUPLE_SIZE() solutions work.
  • Paul Stelian
    Paul Stelian about 6 years
    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)
  • Nic
    Nic over 5 years
    Er, ##__VA_ARGS__ eating the comma before if __VA_ARGS__ is empty is a GCC extension. It's not the standard behavior.
  • H Walters
    H Walters over 5 years
    "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).
  • AntonK
    AntonK almost 5 years
    Nice approach. Unfortunately it doesn't work when NUMARGS is used in #if :(
  • AntonK
    AntonK almost 5 years
    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 :(
  • elhadi dp ıpɐɥןǝ
    elhadi dp ıpɐɥןǝ almost 5 years
    @AntonK can you post what you have done exactly please?
  • qrdl
    qrdl almost 5 years
    @AntonK What do you mean?
  • AntonK
    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
    qrdl almost 5 years
    @AntonK What are you trying to achieve? Looks like XY problem.
  • AntonK
    AntonK almost 5 years
    @qrdl, #define VALUE and then #if NUMARGS(VALUE) == 0 to show #error VALUE is not specified
  • qrdl
    qrdl almost 5 years
    @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.
  • AntonK
    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
    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
    AntonK over 4 years
    @qrdl, #if VALUE == 10 causes compile-time error when VALUE is defined without a value (i.e. #define VALUE)
  • zdf
    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
    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
    Qwertiy over 4 years
    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.
  • poby
    poby about 4 years
    Elegant solution! Works in VS2017. The ## isn't needed in VS2017 as an empty __VA_ARGS__ will automatically remove any preceding comma.
  • Vroomfondel
    Vroomfondel about 4 years
    @HWalters no, there are solutions without the necessity to concatenate.
  • Vroomfondel
    Vroomfondel about 4 years
    IMHO the Microsoft variant fails for zero arguments.
  • Chris Kline
    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
    Vroomfondel about 4 years
    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++;
  • pterodragon
    pterodragon about 4 years
    This 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
    chux - Reinstate Monica almost 4 years
    Any reason for int vs char to drop the /sizeof(int)) and then have (sizeof((char[]){__VA_ARGS__}))?
  • qrdl
    qrdl almost 4 years
    @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!
  • ipid
    ipid over 3 years
    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?
  • Justin
    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
    davernator over 3 years
    Agreed. Great solution! ++.
  • jorgbrown
    jorgbrown about 3 years
    Needs to be tweaked for parens, because int count = NUMARGS( foo(1, 2) ); produces 2 rather than 1. godbolt.org/z/kpBuOm
  • jorgbrown
    jorgbrown about 3 years
    Doesn't work with templates, i.e. NUMARGS( sum<1,2> ); see godbolt.org/z/_AAxmL
  • Justin Time - Reinstate Monica
    Justin Time - Reinstate Monica about 3 years
    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
    Justin Time - Reinstate Monica about 3 years
    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
    Justin Time - Reinstate Monica about 3 years
    I'm not sure if this is the same in C, though, @Vroomfondel, since I lost my bookmark to the most recent draft.
  • Nandee
    Nandee about 3 years
    This won't work as expected with lambdas, function calls or anything else that may contain extra commas in the parameters.
  • Gabriel Staples
    Gabriel Staples almost 3 years
    Duplicate/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
    Andry over 2 years
    Unfortunately gcc does not accept 0 arguments without the extension: at least -std=gnu++11 option must be set.
  • Richard Whitehead
    Richard Whitehead over 2 years
    Superb answer. You can put it into a macro #define NUM_ARGS(...) std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::val‌​ue
  • Berthin
    Berthin about 2 years
    Thanks for pointing out the gnu standard. It solved my problem :D, unlike other answers...
  • EnzoR
    EnzoR over 1 year
    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.
  • Tokubara
    Tokubara over 1 year
    Why we cannot remove PP_NARG_? If I write #define PP_NARG(...) PP_ARG_N(__VA_ARGS__,PP_RSEQ_N()), I get errors.
  • étale-cohomology
    étale-cohomology over 1 year
    This this only works when the arguments are integers? What if each argument has a different type, including structs?
  • qrdl
    qrdl over 1 year
    @é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
  • Kaz
    Kaz about 1 year
    It will not work with GCC if you use -std=c99.
  • Superior
    Superior 11 months
    why not just cast to char[] instead, so you don't need to divide
  • Gabriel Staples
    Gabriel Staples 11 months
    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.
  • Chef Gladiator
    Chef Gladiator 10 months
    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::.
  • Chef Gladiator
    Chef Gladiator 10 months
    In the context of standard C++ this is the right answer. Macro not needed. godbolt.org/z/varbM6r69
  • monkey0506
    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 the C++ headers <cstdlib> and <cassert> rather than the C headers that pull things into the global namespace.
  • Toni Schilling
    Toni Schilling 8 months
    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
    Toni Schilling 8 months
    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))
  • imkzh
    imkzh 6 months
    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.
  • imkzh
    imkzh 6 months
    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);