Detect when multiple enum items map to same value

后端 未结 8 2179
猫巷女王i
猫巷女王i 2020-11-28 13:44

Is there a compile-time way to detect / prevent duplicate values within a C/C++ enumeration?

The catch is that there are multiple items which are initialize

8条回答
  •  感动是毒
    2020-11-28 14:06

    I didn't see "pretty" in your requirements, so I submit this solution implemented using the Boost Preprocessor library.

    As an up-front disclaimer, I haven't used Boost.Preprocessor a whole lot and I've only tested this with the test cases presented here, so there could be bugs, and there may be an easier, cleaner way to do this. I certainly welcome comments, corrections, suggestions, insults, etc.

    Here we go:

    #include 
    
    #define EXPAND_ENUM_VALUE(r, data, i, elem)                          \
        BOOST_PP_SEQ_ELEM(0, elem)                                       \
        BOOST_PP_IIF(                                                    \
            BOOST_PP_EQUAL(BOOST_PP_SEQ_SIZE(elem), 2),                  \
            = BOOST_PP_SEQ_ELEM(1, elem),                                \
            BOOST_PP_EMPTY())                                            \
        BOOST_PP_COMMA_IF(BOOST_PP_NOT_EQUAL(data, BOOST_PP_ADD(i, 1)))
    
    #define ADD_CASE_FOR_ENUM_VALUE(r, data, elem) \
        case BOOST_PP_SEQ_ELEM(0, elem) : break;
    
    #define DEFINE_UNIQUE_ENUM(name, values)                                  \
    enum name                                                                 \
    {                                                                         \
        BOOST_PP_SEQ_FOR_EACH_I(EXPAND_ENUM_VALUE,                            \
                                BOOST_PP_SEQ_SIZE(values), values)            \
    };                                                                        \
                                                                              \
    namespace detail                                                          \
    {                                                                         \
        void UniqueEnumSanityCheck##name()                                    \
        {                                                                     \
            switch (name())                                                   \
            {                                                                 \
                BOOST_PP_SEQ_FOR_EACH(ADD_CASE_FOR_ENUM_VALUE, name, values)  \
            }                                                                 \
        }                                                                     \
    }
    

    We can then use it like so:

    DEFINE_UNIQUE_ENUM(DayOfWeek, ((Monday)    (1))
                                  ((Tuesday)   (2))
                                  ((Wednesday)    )
                                  ((Thursday)  (4)))
    

    The enumerator value is optional; this code generates an enumeration equivalent to:

    enum DayOfWeek
    {
        Monday = 1,
        Tuesday = 2,
        Wednesday,
        Thursday = 4
    };
    

    It also generates a sanity-check function that contains a switch statement as described in Ben Voigt's answer. If we change the enumeration declaration such that we have non-unique enumerator values, e.g.,

    DEFINE_UNIQUE_ENUM(DayOfWeek, ((Monday)    (1))
                                  ((Tuesday)   (2))
                                  ((Wednesday)    )
                                  ((Thursday)  (1)))
    

    it will not compile (Visual C++ reports the expected error C2196: case value '1' already used).

    Thanks also to Matthieu M., whose answer to another question got me interested in the Boost Preprocessor library.

提交回复
热议问题