C++ preprocessor __VA_ARGS__ number of arguments

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

提交回复
热议问题