2

我有一个#define,它生成一个枚举类和一个生成的枚举类的相应输出运算符。(见下文)

#define ENUM(N, T, N1, V1, N2, V2, N3, V3, N4, V4, N5, V5, N6, V6, N7, V7)\
    enum class N : T {\
        N1 = V1,\
        N2 = V2,\
        N3 = V3,\
        N4 = V4,\
        N5 = V5,\
        N6 = V6,\
        N7 = V7,\
    };\
    std::ostream &operator <<(std::ostream &os, const N val);   /* declare function to avoid compiler warning */\
    std::ostream &operator <<(std::ostream &os, const N val) {\
        switch (val) {\
        case N::N1:\
            os << #N1;\
            break;\
        case N::N2:\
            os << #N2;\
            break;\
        case N::N3:\
            os << #N3;\
            break;\
        case N::N4:\
            os << #N4;\
            break;\
        case N::N5:\
            os << #N5;\
            break;\
        case N::N6:\
            os << #N6;\
            break;\
        case N::N7:\
            os << #N7;\
            break;\
        }\
        if (sizeof(T) == 1) {\
            os << '(' << static_cast<int>(val) << ')';\
        } else {\
            os << '(' << static_cast<T>(val) << ')';\
        }\
        return os;\
    }

在这个例子中可以像这里一样使用它:

#include <cstdlib>
#include <iostream>
#include <ostream>

ENUM(Weekdays, unsigned char, Monday, 10, Tuesday, 12, Wednesday, 14, Thursday, 16, Friday, 18, Saterday, 100, Sunday, 101)

int main(const int /*argc*/, const char *const /*argv*/[]) {
    Weekdays    test    = Weekdays::Monday;

    std::cout << test << std::endl;
    std::cout << Weekdays::Tuesday << std::endl;
    std::cout << Weekdays::Sunday << std::endl;

    return EXIT_SUCCESS;
}

这里生成的输出:

Monday(10)
Tuesday(12)
Sunday(101)

我的解决方案有一些限制:

  • 每个枚举都需要一个初始化值
  • 固定为 7 个枚举值

对于更普遍的用法,我有两个问题。特别是第二个将极大地增加可用性。

这里有我的任何问题:

  1. 如何避免为每个枚举值定义一个初始化值?
    (就像在真正的枚举中一样)
  2. 有什么想法可以概括 #define 以使用任意数量的值?

我正在等待您对我的代码的评论和改进建议。
雷纳

4

3 回答 3

2

保持相对接近你现在拥有的东西,你可以利用 Boost.Preprocessor 中的 BOOST_PP_SEQ_FOR_EACH 宏,它看起来像这样:

#include <boost/preprocessor.hpp>

#define ENUM_FIELD(I,_,F) F,
#define ENUM_OUTPUT_CASE(I,N,F) case N::F: os << BOOST_PP_STRINGIZE(F); break;

#define ENUM(N, T, ARGS) \
enum class N : T {\
BOOST_PP_SEQ_FOR_EACH(ENUM_FIELD,N,ARGS)\
};\
std::ostream &operator <<(std::ostream &os, const N val) {\
    switch (val) {\
    BOOST_PP_SEQ_FOR_EACH(ENUM_OUTPUT_CASE,N,ARGS)\
    }\
    \
    os << '(' << static_cast<int>(val) << ')';\
    return os;\
}

ENUM(Weekdays, unsigned char, (Monday)(Tuesday)(Wednesday)(Thursday)(Friday)(Saturday)(Sunday))

这消除了提供值的重复和可能性。整个事情更短,可以说是以降低可读性和可能更难调试为代价——我不会权衡使用这些宏的利弊。

请注意,我已经更改了将参数传递给 ENUM 宏的方式:现在这是一个 Boost.Preprocessor序列。您应该最多可以传递 256 个元素;有关更多信息和更多适用于序列的宏,请参阅文档。

于 2021-08-09T10:20:02.983 回答
1

我让它为我工作。添加了一些特殊功能:

  • 用于打开/关闭输出枚举值的操纵器
    (在枚举后面的括号中)
  • 非法值的输出
    (不应发生:请参阅可能发生的代码)

这是我的完整解决方案:

#include <cstdlib>
#include <iostream>
#include <ostream>

#include <boost/preprocessor.hpp>
#include <boost/preprocessor/tuple/elem.hpp>

class EnumShowValue {
private:
    static bool showValueFlag;
public:
    explicit EnumShowValue(const bool flag) { EnumShowValue::showValueFlag  = flag; }

    static bool showValue() { return EnumShowValue::showValueFlag; }
};
bool    EnumShowValue::showValueFlag    = false;

inline std::ostream &operator <<(std::ostream &os, const EnumShowValue &) { return os; }

#define ENUM_FIELD(I,_,F)\
    BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(F),2),\
                    BOOST_PP_TUPLE_ELEM(0,F)=BOOST_PP_TUPLE_ELEM(1,F),\
                    BOOST_PP_TUPLE_ELEM(0,F)),

#define ENUM_OUTPUT_CASE(I,N,F)\
    case N::BOOST_PP_TUPLE_ELEM(0,F):\
        os << BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(0,F));\
        break;

#define ENUM(N, T, ARGS) \
enum class N : T {\
BOOST_PP_SEQ_FOR_EACH(ENUM_FIELD,N,ARGS)\
};\
std::ostream &operator <<(std::ostream &os, const N val);\
std::ostream &operator <<(std::ostream &os, const N val) {\
    switch (val) {\
    BOOST_PP_SEQ_FOR_EACH(ENUM_OUTPUT_CASE,N,ARGS)\
    default:\
        os << "illegal value: " << BOOST_PP_STRINGIZE(N);\
        if (!EnumShowValue::showValue()) {\
            os << '(';\
            if (sizeof(T) == 1) {\
                os << static_cast<int>(val);\
            } else {\
                os << static_cast<T>(val);\
            }\
            os << ')';\
        }\
    }\
    if (EnumShowValue::showValue()) {\
                    os << '(';\
                    if (sizeof(T) == 1) {\
                        os << static_cast<int>(val);\
                    } else {\
                        os << static_cast<T>(val);\
                    }\
                    os << ')';\
    }\
    return os;\
}

ENUM(Weekdays, unsigned char, ((Monday, 101))((Tuesday))((Wednesday))((Thursday))((Friday))((Saturday, 200))((Sunday)))

int main(const int /*argc*/, const char *const /*argv*/[]) {

    std::cout << Weekdays::Monday << std::endl;
    std::cout << Weekdays::Tuesday << std::endl;
    std::cout << Weekdays::Wednesday << std::endl;
    std::cout << Weekdays::Thursday << std::endl;
    std::cout << Weekdays::Friday << std::endl;
    std::cout << Weekdays::Saturday << std::endl;
    std::cout << Weekdays::Sunday << std::endl;
    std::cout << Weekdays(99) << std::endl;

    std::cout << EnumShowValue(true);
    std::cout << Weekdays::Monday << std::endl;
    std::cout << Weekdays::Tuesday << std::endl;
    std::cout << Weekdays::Wednesday << std::endl;
    std::cout << Weekdays::Thursday << std::endl;
    std::cout << Weekdays::Friday << std::endl;
    std::cout << Weekdays::Saturday << std::endl;
    std::cout << EnumShowValue(false);
    std::cout << Weekdays::Sunday << std::endl;
    std::cout << Weekdays(-1) << std::endl;

    return EXIT_SUCCESS;
}

和相应的输出:

Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
illegal value: Weekdays(99)
Monday(101)
Tuesday(102)
Wednesday(103)
Thursday(104)
Friday(105)
Saturday(200)
Sunday
illegal value: Weekdays(255)
于 2021-08-10T07:59:50.560 回答
0

如果你不需要知道这个编译时间,你可以使用像 Protobuf 这样的库来定义你的枚举。C++ 中的 Protobuf 支持可以用作反射形式的枚举描述符。这两篇文章描述了使用 Protobuf(12)的可能解决方案。

编辑:我忘记了如果您需要编译时间,还有另一个库可能对您有用。它被称为Frozen并提供编译时间映射。您可能能够生成一些定义映射的代码并使用它将枚举值转换为字符串。

于 2021-08-09T10:58:45.493 回答