使用
boost::preprocessor可以实现以下优雅的解决方案:
步骤1:包含头文件:
第二步:使用以下语法声明枚举对象:
MakeEnum( TestData,
(x)
(y)
(z)
);
步骤三:使用您的数据:
获取元素数量:
td::cout << "Number of Elements: " << TestDataCount << std::endl;
获取相关的字符串:
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;
从关联的字符串中获取枚举值:
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;
这看起来干净而紧凑,没有额外的文件需要包含。
我在EnumUtilities.h中编写的代码如下:
#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; \
};
有一些限制,比如boost::preprocessor的限制。在这种情况下,常量列表不能超过64个元素。
按照相同的逻辑,您也可以考虑创建稀疏枚举:
#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."; \
}; \
};
在这种情况下,语法是:
MakeEnumEx(TestEnum,
((x,))
((y,=1000))
((z,))
);
使用方法与上面类似(减去eName##2Enum函数,您可以尝试从前面的语法推断出来)。
我在Mac和Linux上测试了它,但请注意boost::preprocessor可能不是完全可移植的。
SOME_ENUM(XX)
正是X-macro(确切地说,是传递XX
函数而不是使用#def``#undef
的“用户形式”),然后再将整个X-MACRO传递给DEFINE_ENUM来使用它。并不是要贬低这种解决方案 - 它很有效。只是为了澄清这是X macros的用法。 - BeeOnRopeXX
作为参数传递的优点是前者模式可用于宏展开中。请注意,仅有的其他与此解决方案一样简洁的解决方案都需要创建并多次包含一个单独的文件以定义新枚举类型。 - pmttavara#define DEFINE_ENUM(EnumType) ...
,将ENUM_DEF(...)
替换为EnumType(...)
,并让用户说#define SomeEnum(XX) ...
。当后面跟着括号时,C 预处理器会在上下文中扩展SomeEnum
为宏调用,并在其他情况下扩展为常规标记。(当然,如果用户喜欢使用SomeEnum(2)
来转换为枚举类型而不是(SomeEnum)2
或static_cast<SomeEnum>(2)
,这会导致问题。) - pmttavara#define
和#undef
。您是否不同意“用户表单”(例如建议在此文章底部)是x-macro的一种类型?我肯定也一直称其为x-macro,在我最近使用的C代码库中,它是最常见的形式(这显然是一种有偏见的观察)。尽管如此,我可能一直对原始帖子的解析有误。 - BeeOnRope