C++ preprocessor __VA_ARGS__ number of arguments

后端 未结 12 2106
一向
一向 2020-11-22 08:03

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

相关标签:
12条回答
  • 2020-11-22 08:24

    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.

    0 讨论(0)
  • 2020-11-22 08:28

    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
    
    0 讨论(0)
  • 2020-11-22 08:29

    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
    
    0 讨论(0)
  • 2020-11-22 08:36

    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.

    0 讨论(0)
  • 2020-11-22 08:36

    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.

    0 讨论(0)
  • 2020-11-22 08:40

    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.

    0 讨论(0)
提交回复
热议问题