Easy way to use variables of enum types as string in C?

前端 未结 19 2024
太阳男子
太阳男子 2020-11-22 08:47

Here\'s what I am trying to do:

typedef enum { ONE, TWO, THREE } Numbers;

I am trying to write a function that would do a switch case sim

相关标签:
19条回答
  • 2020-11-22 09:22

    The use of boost::preprocessor makes possible an elegant solution like the following:

    Step 1: include the header file:

    #include "EnumUtilities.h"
    

    Step 2: declare the enumeration object with the following syntax:

    MakeEnum( TestData,
             (x)
             (y)
             (z)
             );
    

    Step 3: use your data:

    Getting the number of elements:

    td::cout << "Number of Elements: " << TestDataCount << std::endl;
    

    Getting the associated string:

    std::cout << "Value of " << TestData2String(x) << " is " << x << std::endl;
    std::cout << "Value of " << TestData2String(y) << " is " << y << std::endl;
    std::cout << "Value of " << TestData2String(z) << " is " << z << std::endl;
    

    Getting the enum value from the associated string:

    std::cout << "Value of x is " << TestData2Enum("x") << std::endl;
    std::cout << "Value of y is " << TestData2Enum("y") << std::endl;
    std::cout << "Value of z is " << TestData2Enum("z") << std::endl;
    

    This looks clean and compact, with no extra files to include. The code I wrote within EnumUtilities.h is the following:

    #include <boost/preprocessor/seq/for_each.hpp>
    #include <string>
    
    #define REALLY_MAKE_STRING(x) #x
    #define MAKE_STRING(x) REALLY_MAKE_STRING(x)
    #define MACRO1(r, data, elem) elem,
    #define MACRO1_STRING(r, data, elem)    case elem: return REALLY_MAKE_STRING(elem);
    #define MACRO1_ENUM(r, data, elem)      if (REALLY_MAKE_STRING(elem) == eStrEl) return elem;
    
    
    #define MakeEnum(eName, SEQ) \
        enum eName { BOOST_PP_SEQ_FOR_EACH(MACRO1, , SEQ) \
        last_##eName##_enum}; \
        const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
        static std::string eName##2String(const enum eName eel) \
        { \
            switch (eel) \
            { \
            BOOST_PP_SEQ_FOR_EACH(MACRO1_STRING, , SEQ) \
            default: return "Unknown enumerator value."; \
            }; \
        }; \
        static enum eName eName##2Enum(const std::string eStrEl) \
        { \
            BOOST_PP_SEQ_FOR_EACH(MACRO1_ENUM, , SEQ) \
            return (enum eName)0; \
        };
    

    There are some limitation, i.e. the ones of boost::preprocessor. In this case, the list of constants cannot be larger than 64 elements.

    Following the same logic, you could also think to create sparse enum:

    #define EnumName(Tuple)                 BOOST_PP_TUPLE_ELEM(2, 0, Tuple)
    #define EnumValue(Tuple)                BOOST_PP_TUPLE_ELEM(2, 1, Tuple)
    #define MACRO2(r, data, elem)           EnumName(elem) EnumValue(elem),
    #define MACRO2_STRING(r, data, elem)    case EnumName(elem): return BOOST_PP_STRINGIZE(EnumName(elem));
    
    #define MakeEnumEx(eName, SEQ) \
        enum eName { \
        BOOST_PP_SEQ_FOR_EACH(MACRO2, _, SEQ) \
        last_##eName##_enum }; \
        const int eName##Count = BOOST_PP_SEQ_SIZE(SEQ); \
        static std::string eName##2String(const enum eName eel) \
        { \
            switch (eel) \
            { \
            BOOST_PP_SEQ_FOR_EACH(MACRO2_STRING, _, SEQ) \
            default: return "Unknown enumerator value."; \
            }; \
        };  
    

    In this case, the syntax is:

    MakeEnumEx(TestEnum,
               ((x,))
               ((y,=1000))
               ((z,))
               );
    

    Usage is similar as above (minus the eName##2Enum function, that you could try to extrapolate from the previous syntax).

    I tested it on mac and linux, but be aware that boost::preprocessor may not be fully portable.

    0 讨论(0)
提交回复
热议问题