Search code examples
c++templatesboostboost-preprocessor

Construct boost::fusion::map (and more) through custom macro


I have a traits class which contains all signals each class has inside a boost::fusion::map.

Example:

template <typename T> struct EventTraits;

class SomeClass;

template <> struct EventTraits<SomeClass>
{
    struct Started;
    struct Finished;
    typedef boost::fusion::map<
        boost::fusion::pair<Started, boost::signals2::signal<void()>>,
        boost::fusion::pair<Finished, boost::signals2::signal<void(int)>>
    > Events;
};

Since I will need this kind of traits specialization often, I would like to have a macro which saves me some typing, like this imaginary example:

CONSTRUCT_EVENTS(
    SomeClass,
    (Started, void())
    (Finished, void(int))
)

How could I implement such a CONSTRUCT_EVENTS macro? As a starting point I had a look at BOOST_FUSION_ADAPT_STRUCT and then at Boost Preprocessor, but I have not yet used the latter so I hope someone can guide me.


Solution

  • Here's something that should work:

    #include <boost/fusion/container/map.hpp>
    #include <boost/preprocessor.hpp>
    #include <boost/signals2.hpp>
    
    template <typename T> struct EventTraits;
    
    #define DECLARE_EVENT_STRUCT(r, data, elem) struct BOOST_PP_TUPLE_ELEM(2,0,elem);
    #define DECLARE_MAP_ITEM(r, data, i, elem) BOOST_PP_COMMA_IF(i) boost::fusion::pair<BOOST_PP_TUPLE_ELEM(2,0,elem), boost::signals2::signal<BOOST_PP_TUPLE_ELEM(2,1,elem)> >
    #define CONSTRUCT_EVENTS_(Name, EventSeq)                          \
        class Name;                                                    \
        template <> struct EventTraits<Name>                           \
        {                                                              \
            BOOST_PP_SEQ_FOR_EACH(DECLARE_EVENT_STRUCT, _, EventSeq)   \
            typedef boost::fusion::map                                 \
            <                                                          \
                BOOST_PP_SEQ_FOR_EACH_I(DECLARE_MAP_ITEM, _, EventSeq) \
            > Events;                                                  \
        };                                                             \
    /***/
    
    //! Stuff to transform (A,B)(C,D) into ((A,B))((C,D)) so BOOST_PP_SEQ_FOR_EACH can be used. (sequence of tuples)
    #define ADD_PAREN_1(A, B) ((A, B)) ADD_PAREN_2 
    #define ADD_PAREN_2(A, B) ((A, B)) ADD_PAREN_1 
    #define ADD_PAREN_1_END 
    #define ADD_PAREN_2_END 
    #define CONSTRUCT_EVENT(Name, EventSeq)                            \
    CONSTRUCT_EVENTS_(Name, BOOST_PP_CAT(ADD_PAREN_1 EventSeq,_END))   \
    /***/
    
    //! Check the output (I use this on visual studio)
    #pragma  message(BOOST_PP_STRINGIZE((CONSTRUCT_EVENT(SomeClass, (Started, void())(Finished, void(int))))))
    
    //! Result (with formatting applied)
    class SomeClass; 
    template <> 
    struct EventTraits<SomeClass> 
    {
        struct Started; 
        struct Finished; 
        typedef boost::fusion::map 
            <
                boost::fusion::pair<Started, boost::signals2::signal<void()> >
              , boost::fusion::pair<Finished, boost::signals2::signal<void(int)> >
            > Events; 
    };
    
    int main()
    {
        return 0;
    }