C++ preprocessor __VA_ARGS__ number of arguments

后端 未结 12 2116
一向
一向 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:40

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

    herein a simple way to count 0 or more arguments of VA_ARGS, my exemple 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
             ...
    
    0 讨论(0)
  • 2020-11-22 08:45

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

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

    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

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

    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]!='\\') {
              ++i;
              break;
            }
            ++i;
          }
        }
        ++i;
      }
    
      return result;
    }
    
    #define MACRO(...) \
    { \
      int count = countArgs(#__VA_ARGS__); \
      printf("NUM ARGS: %d\n",count); \
    }
    
    0 讨论(0)
提交回复
热议问题