Using BOOST_PP_SEQ_FOR_EACH (or an alternative) when sequence contains commas

782 Views Asked by At

I'm trying to create a macro that I can use to automatically create stringification functions for scoped enums. Not being especially fond of working with macros, I've been looking through the code of the Boost.Fusion library for inspiration (in particular, BOOST_FUSION_ADAPT_STRUCT). When the enums have default values (an integer sequence starting from zero), I have a working implementation:

#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

#define XSTR(s) STR(s)
#define STR(s) #s
#define ENUM_FLAG(r, data, elemName) elemName,
#define ENUM_CASE(r, data, elemName) case data::elemName: return XSTR(elemName);
#define STRINGIFIED_ENUM(NAME_SEQ,                                      \
                         ATTRIBUTES_SEQ)                                \
    enum class NAME_SEQ {                                               \
        BOOST_PP_SEQ_FOR_EACH(ENUM_FLAG,,ATTRIBUTES_SEQ)                \
        SIZE = BOOST_PP_SEQ_SIZE(ATTRIBUTES_SEQ)                        \
    };                                                                  \
    std::string enumToString(const NAME_SEQ& e) {                       \
        switch(e) {                                                     \
            BOOST_PP_SEQ_FOR_EACH(ENUM_CASE,NAME_SEQ,ATTRIBUTES_SEQ)    \
        default: return "";                                             \
        }                                                               \
    }

Here is some example usage on coliru.

However, this isn't complete enough for my purposes: I need a version that will allow custom enum values, and I'd like to be able to call this with something like

STRINGIFIED_CUSTOM_ENUM(myEnum, (a,1)(b,3)(c,5))

I know this should be possible because this is roughly the signature of BOOST_FUSION_DEFINE_STRUCT and BOOST_FUSION_ADAPT_STRUCT (though I can't make heads or tails of those macros in the boost source code). However, so far I can only get working a solution that requires putting each element of the sequence in parenthesis:

STRINGIFIED_CUSTOM_ENUM(myEnum, ((a,1))((b,3))((c,5)))

An example is here on coliru, and uses code like this (note that I'm just calling the macro STRINGIFIED_ENUM here because I'm just testing them separately for now:

#include <boost/preprocessor/seq/size.hpp>
#include <boost/preprocessor/seq/for_each.hpp>

#define XSTR(s) STR(s)
#define STR(s) #s
#define ELEM_PAIR_NAME(elemPair) BOOST_PP_TUPLE_ELEM(2,0,elemPair)
#define ELEM_PAIR_VALUE(elemPair) BOOST_PP_TUPLE_ELEM(2,1,elemPair)
#define ENUM_FLAG(r, data, elemPair) ELEM_PAIR_NAME(elemPair) = ELEM_PAIR_VALUE(elemPair),
#define ENUM_CASE(r, data, elemPair) case data::ELEM_PAIR_NAME(elemPair): return XSTR(ELEM_PAIR_NAME(elemPair));
#define STRINGIFIED_ENUM(NAME_SEQ,                                      \
                         ATTRIBUTES_SEQ)                                \
    enum class NAME_SEQ {                                               \
        BOOST_PP_SEQ_FOR_EACH(ENUM_FLAG,,ATTRIBUTES_SEQ)                \
        SIZE = BOOST_PP_SEQ_SIZE(ATTRIBUTES_SEQ)                        \
    };                                                                  \
    std::string enumToString(const NAME_SEQ& e) {                       \
        switch(e) {                                                     \
            BOOST_PP_SEQ_FOR_EACH(ENUM_CASE,NAME_SEQ,ATTRIBUTES_SEQ)    \
        default: return "";                                             \
        }                                                               \
    }

This has the advantage of a) working and b) being pretty simple, but I find having to add these extra parenthesis pretty annoying. If I take them away, by the way, errors like

main.cpp:24:40: error: macro "BOOST_PP_SEQ_SIZE_0" passed 2 arguments, but takes just 1
 STRINGIFIED_ENUM(myEnum,(a,1)(b,3)(c,5))

and so on are inevitable. I take it the normal macro thing is happening here, where without the extra parenthesis the macro treats the commas as a delimiter between macro arguments rather than part of a nested macro argument.

Is there a pretty simple way through this obstacle, preferrably involving the least amount of macro nesting possible?

0

There are 0 best solutions below