Search code examples
boost-preprocessor

Boost preprocessor skip if variadic is empty


I have the following boost preprocessor macro to generate a function

extern "C" EXPORT out name(BOOST_PP_SEQ_FOR_EACH_I(PARAMETER_LIST, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)))

This works great unless if __VA_ARGS__ is empty. After some searching I found a way to count the numer of arguments in __VA_ARGS__ using BOOST_PP_VARIADIC_SIZE. After some thinking I wrote this MACRO:

extern "C" EXPORT out name(BOOST_PP_IF(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), BOOST_PP_SEQ_FOR_EACH_I(PARAMETER_LIST, 0, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__)), void))

I think this should work, however I keep getting the following warning

 warning C4002: too many actual parameters for macro 'BOOST_PP_IIF_1'

Although this is a warning it still seems to break the preprocessor. When passing multiple arguments it will only process the first one. I find this so strange, how will adding this if break everything is such a weird way? I've checked the comma's and brackets hundred times but they seem fine. How can I fix this preprocessor?

http://www.boost.org/doc/libs/1_54_0/libs/preprocessor/doc/ref/if.html

Edit: this regression seems relevant: https://svn.boost.org/trac/boost/ticket/8606


Solution

  • implementation:

    #include <boost/preprocessor.hpp>
    
    // based on the: http://gustedt.wordpress.com/2010/06/08/detect-empty-macro-arguments
    #define __ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15
    #define __HAS_COMMA(...) __ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0)
    #define __TRIGGER_PARENTHESIS_(...) ,
    #define __PASTE5(_0, _1, _2, _3, _4) _0 ## _1 ## _2 ## _3 ## _4
    #define __IS_EMPTY_CASE_0001 ,
    #define __IS_EMPTY(_0, _1, _2, _3) __HAS_COMMA(__PASTE5(__IS_EMPTY_CASE_, _0, _1, _2, _3))
    
    #define TUPLE_IS_EMPTY(...) \
        __IS_EMPTY( \
            /* test if there is just one argument, eventually an empty one */ \
            __HAS_COMMA(__VA_ARGS__), \
            /* test if _TRIGGER_PARENTHESIS_ together with the argument adds a comma */ \
            __HAS_COMMA(__TRIGGER_PARENTHESIS_ __VA_ARGS__),                 \
            /* test if the argument together with a parenthesis adds a comma */ \
            __HAS_COMMA(__VA_ARGS__ (/*empty*/)), \
            /* test if placing it between _TRIGGER_PARENTHESIS_ and the parenthesis adds a comma */ \
            __HAS_COMMA(__TRIGGER_PARENTHESIS_ __VA_ARGS__ (/*empty*/)) \
        )
    
    #define __GEN_EMPTY_ARGS(...) \
        void
    
    #define __GEN_NONEMPTY_ARGS_CB(unused, data, idx, elem) \
        BOOST_PP_COMMA_IF(idx) elem arg##idx
    
    #define __GEN_NONEMPTY_ARGS(seq) \
        BOOST_PP_SEQ_FOR_EACH_I( \
             __GEN_NONEMPTY_ARGS_CB \
            ,~ \
            ,seq \
        )
    
    #define GEN(out, name, ...) \
        extern "C" EXPORT out name( \
            BOOST_PP_IF( \
                 TUPLE_IS_EMPTY(__VA_ARGS__) \
                ,__GEN_EMPTY_ARGS \
                ,__GEN_NONEMPTY_ARGS \
            )(BOOST_PP_TUPLE_TO_SEQ((__VA_ARGS__))) \
        ) {}
    
    // test
    GEN(void, finc0, int, char, long)
    GEN(void, func1)
    

    output:

    extern "C" EXPORT void finc0( int arg0 , char arg1 , long arg2 ) {}
    extern "C" EXPORT void func1( void ) {}