从C ++宏创建字符串列表和枚举列表

为了使我的代码更短更容易更改,我想替换类似的东西

enum{ E_AAA, E_BBB, E_CCC }; static const char *strings{"AAA", "BBB", "CCC" }; 

使用宏,如INIT(AAA,BBB,CCC); 但是当我尝试使用变量参数和字符串化做一个宏时,我得到一个错误,因为没有声明参数。

有关如何做到这一点的任何想法?

这是我几天前学到的解决方案。 参加您的问题的简化版本是:

 #define ENUM_MACRO(name, v1, v2, v3, v4, v5, v6, v7)\ enum name { v1, v2, v3, v4, v5, v6, v7};\ const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7}; ENUM_MACRO(Week, Sun, Mon, Tue, Wed, Thu, Fri, Sat); 

但是你可以使用函数调用来改进版本,如下所示:

 #define ENUM_MACRO(name, v1, v2, v3, v4, v5, v6, v7)\ enum name { v1, v2, v3, v4, v5, v6, v7};\ const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7};\ const char *name##ToString(value) { return name##Strings[value]; } ENUM_MACRO(Week, Sun, Mon, Tue, Wed, Thu, Fri, Sat); 

这将成长为:

  enum Week { Sun, Mon, Tue, Wed, Thu, Fri, Sat}; const char *WeekStrings[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; const char *WeekToString(value) { return WeekStrings[value]; }; 

您甚至可以为第一个元素使用偏移量 ,如下所示:

 #define ENUM_MACRO(name, offset, v1, v2, v3, v4, v5, v6, v7)\ enum name { v1 = offset, v2, v3, v4, v5, v6, v7};\ const char *name##Strings[] = { #v1, #v2, #v3, #v4, #v5, #v6, #v7};\ const char *name##ToString(value) { return name##Strings[value - offset ]; } ENUM_MACRO(Week, 1, Sun, Mon, Tue, Wed, Thu, Fri, Sat); 

我希望这有帮助。

小心,Beco

参考:

打印月份问题 ,由Kush,Danny Varod回答

你可以用一些宏魔法来做到这一点:

 #define FRUITS \ etype(Unknown), \ etype(Apple), \ etype(Orange), \ etype(Banana), \ etype(Apricot), \ etype(Mango) #define etype(x) F_##x typedef enum { FRUITS } Fruit; #undef etype #define etype(x) #x static const char *strFruit[] = { FRUITS }; 

这是一个测试程序:

 #include  #include  #include  #define FRUITS \ etype(Unknown), \ etype(Apple), \ etype(Orange), \ etype(Banana), \ etype(Apricot), \ etype(Mango) #define etype(x) F_##x typedef enum { FRUITS } Fruit; #undef etype #define etype(x) #x static const char *strFruit[] = { FRUITS }; const char *enum2str (Fruit f) { return strFruit[static_cast(f)]; } Fruit str2enum (const char *f) { const int n = sizeof(strFruit) / sizeof(strFruit[0]); for (int i = 0; i < n; ++i) { if (strcmp(strFruit[i], f) == 0) return (Fruit) i; } return F_Unknown; } int main (int argc, char *argv[]) { std::cout << "I like " << enum2str(F_Mango) << std::endl; std::cout << "I do not like " << enum2str(F_Banana) << std::endl; std::vector v; v.push_back("Apple"); v.push_back("Mango"); v.push_back("Tomato"); for (int i = 0; i < v.size(); ++i) { const Fruit f = str2enum(v[i]); if (f == F_Unknown) std::cout << "Is " << v[i] << " a fruit?" << std::endl; else std::cout << v[i] << " is a fruit" << std::endl; } return 0; } 

它输出:

 I like Mango I do not like Banana Apple is a fruit Mango is a fruit Is Tomato a fruit? 

这是我的解决方案:

 #define FRUITS(fruit) \ fruit(Apple) \ fruit(Orange) \ fruit(Banana) #define CREATE_ENUM(name) \ F_##name, #define CREATE_STRINGS(name) \ #name, 

诀窍在于,“水果”是宏“水果”的一个参数,将被你传递给它的东西取代。 例如:

 FRUITS(CREATE_ENUM) 

将扩展到这个:

 F_Apple, F_Orange, F_Banana, 

让我们创建枚举和字符串数组:

 enum fruit { FRUITS(CREATE_ENUM) }; const char* fruit_names[] = { FRUITS(CREATE_STRINGS) }; 

一种方法是使用X-Macros ,它基本上是一种定义宏的方法,然后用于生成比简单宏容易允许的更复杂的结构。 这是一个完全按照你要求做的例子 。

这是Boost.Preprocessor的解决方案:

 #include  #define DEFINE_ENUM_DECL_VAL(r, name, val) BOOST_PP_CAT(name, BOOST_PP_CAT(_, val)) #define DEFINE_ENUM_VAL_STR(r, name, val) BOOST_PP_STRINGIZE(val) #define DEFINE_ENUM(name, val_seq) \ enum name { \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(DEFINE_ENUM_DECL_VAL, name, val_seq)) \ }; \ static const char* BOOST_PP_CAT(name, _strings[] = ) { \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(DEFINE_ENUM_VAL_STR, name, val_seq)) \ }; DEFINE_ENUM(E, (AAA)(BBB)(CCC)) 

(AAA)(BBB)(CCC)是树元素AAA,BBB和CCC的Boost.Preprocessor序列; 宏将枚举名称附加到它的模态:

 enum E { E_AAA, E_BBB, E_CCC }; static const char* E_strings[] = { "AAA", "BBB", "CCC" }; 

处理此问题的一种方法是定义列表宏 ,即扩展到另一个宏以供用户定义的内容。 例如:

 #define MY_LIST MY_ENTRY(AAA) MY_ENTRY(BBB) MY_ENTRY(CCC) 

要定义enum

 #define MY_ENTRY(x) E_##x, enum name { MY_LIST NUMBER_OF_ELEMENTS /* Needed to eat trailing comma (not needed in C99, but in C++) */ }; #undef MY_ENTRY 

要定义字符串:

 #define MY_ENTRY(x) #x, static const char *strings[] = { MY_LIST }; #undef MY_ENTRY 

就个人而言,我觉得这比X宏更容易使用,因为这不依赖于包含文件魔法。

对于一个简单的解决方案,我建议像X-Macros这样的东西。

对于一个更复杂的解决方案,它增加了一些其他function(如范围检查,增强的类型安全性,可选的相关数据等),有一个建议的(但从未完成) Boost.Enum库 。

我有点迟到了,但这是另一个建议。
它创建了一个强类型枚举类,比如说MyEnumName和一个伴随静态助手类Enumator
它比以前的答案更大,因为它具有更多function,例如用于从/转换为字符串的流操作符。
请注意,由于使用了索引序列,它依赖于c ++ 14标准。

用法:

 /* One line definition - no redundant info */ ENUM_DEFINE(WeekDay /*first item is enum name*/, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday); /* works seemlessly with streams (good for logging) */ auto dayOne = WeekDay::Sunday; std::cout << "day of week is: " << day_of_week; /* explicit construction from string using WeekDay_enum companion class*/ auto dayTwo = Enumator::fromString("Tuesday"); /*Iterate over all enum values using Enumator companion class*/ std::cout << "Days of the week are:\n" for (auto enumVal : Enumator::getValues()) { std::cout << enumVal << "\n"; } 

资源:

  #include  #include  #include  #include  template using isEnum = typename std::enable_if::value>::type; template> constexpr static int enumSize() { return 0; } template> inline static std::string getEnumStringValues() { return ""; } /*Enum companion class to hold the methods that can't be declared in an enum*/ template* = nullptr> class Enumator { Enumator() = delete; /* prevents instantiation */ public: constexpr static int size() { return enumSize(); } /* list of all enum values a string */ static auto const& getValuesStr() { static std::array values; if (values[0].empty()) { std::string valuesStr = getEnumStringValues(); std::stringstream ss(valuesStr); for (auto& value : values) { std::getline(ss, value, ','); } } return values; }; /* list of all enum values */ static auto const& getValues() { static std::array values{ make_array(std::make_index_sequence()) }; return values; }; /* To/from string conversion */ constexpr static std::string const& toString(EnumType arg) { return getValuesStr()[static_cast(arg)]; } static EnumType fromString(std::string const& val) { /* Attempt at converting from string value */ auto const& strValues = getValuesStr(); for (unsigned int i = 0; i < strValues.size(); i++) { if (val == strValues[i]) { return static_cast(i); } } throw std::runtime_error("No matching enum value found for token: " + val); } private: /* Helper method to initialize array of enum values */ template static auto make_array(std::index_sequence) { return std::array{{static_cast(Idx)...}}; } }; template* = nullptr> inline std::istream& operator>> (std::istream& input, EnumType& arg) { std::string val; input >> val; arg = Enumator::fromString(val); return input; } template* = nullptr> inline std::ostream& operator<< (std::ostream& output, const EnumType& arg) { return output << Enumator::toString(arg); } #define ENUM_DEFINE(EnumName,...)\ \ enum class EnumName;\ \ template<>\ constexpr int enumSize() {\ /*Trick to get the number of enum members:*/\ /*dump all the enum values in an array and compute its size */\ enum EnumName { __VA_ARGS__ }; \ EnumName enumArray[]{ __VA_ARGS__ }; \ return sizeof(enumArray) / sizeof(enumArray[0]); \ }\ \ template<>\ inline std::string getEnumStringValues() { return #__VA_ARGS__; }\ \ enum class EnumName : int { __VA_ARGS__ }