我知道这has been asked before不止一次对SO,但我无法找到一个明确的问题寻找一个当前解决这个问题与C ++ 11,所以在这里,我们再次去..
我们可以方便地获得与C ++ 11枚举的字符串值?
即有没有(现在)在C ++ 11的任何内置功能,使我们能够获得枚举类型的字符串表示形式
typedef enum {Linux, Apple, Windows} OS_type;
OS_type myOS = Linux;
cout << myOS
这将打印到控制台Linux
?
长期存在的和不必要的缺乏在C ++(和C)的通用枚举到字符串特征的是一个痛苦。 C ++ 11,因为我知道都不会对C ++ 14并没有解决这个问题,而据。
个人而言,我会使用代码生成解决这个问题。 C预处理器是一个办法 - 你可以看到在这里评论该链接的其他一些答案。但实际上我更愿意只写我自己的代码生成专门为枚举。如需要它可以很容易生成to_string (char*)
,from_string
,ostream operator<<
,istream operator<<
,is_valid
,和更多的方法。这种方法可以非常灵活和强大,但它绝对强制执行的一致性在许多枚举的一个项目,这样会导致没有运行时的成本。
不要使用它Python的出色的“灰鲭鲨”包,或在Lua如果你为轻量,或CPP,如果你是对的依赖,或CMake的自己生成代码设施。很多方面,但是这一切都归结为同样的事情:你需要自己生成的代码 - C ++不会为你做这个(很遗憾)。
在我看来,最易于维护的办法是写一个辅助函数:
const char* get_name(OS_type os) {
switch (os) {
case Linux: return "Linux";
case Apple: return "Apple";
case Windows: return "Windows";
}
}
这是不是实现了“默认”的情况下是一个好主意,因为这样做将确保你得到一个编译器警告,如果你忘了执行的情况下(与合适的编译器和编译器设置)。
我喜欢使用C预处理器,这是我第一次在这里看到一个黑客:http://blogs.msdn.com/b/vcblog/archive/2008/04/30/enums-macros-unicode-and-token-pasting.aspx。
它使用标记粘贴运算符#。
// This code defines the enumerated values:
#define MY_ENUM(x) x,
enum Fruit_Type {
MY_ENUM(Banana)
MY_ENUM(Apple)
MY_ENUM(Orange)
};
#undef MY_ENUM
// and this code defines an array of string literals for them:
#define MY_ENUM(x) #x,
const char* const fruit_name[] = {
MY_ENUM(Banana)
MY_ENUM(Apple)
MY_ENUM(Orange)
};
#undef MY_ENUM
// Finally, here is some client code:
std::cout << fruit_name[Banana] << " is enum #" << Banana << "\n";
// In practice, those three "MY_ENUM" macro calls will be inside an #include file.
坦率地说,它的丑陋和。但你最终在一个包含文件中只出现一次输入你的枚举,这是更易于维护。
顺便说一句,这MSDN博客链接(见上文)用户做出了一招,使整个事情更漂亮,并且避免的#includes评论:
#define Fruits(FOO) \
FOO(Apple) \
FOO(Banana) \
FOO(Orange)
#define DO_DESCRIPTION(e) #e,
#define DO_ENUM(e) e,
char* FruitDescription[] = {
Fruits(DO_DESCRIPTION)
};
enum Fruit_Type {
Fruits(DO_ENUM)
};
// Client code:
std::cout << FruitDescription[Banana] << " is enum #" << Banana << "\n";
(我只注意到0x17de的回答也使用标记粘贴操作)
下面是使用命名空间和结构一个简单的例子。一类是每个枚举项创建。在这个例子中我选择int
作为ID的类型。
#include <iostream>
using namespace std;
#define ENUMITEM(Id, Name) \
struct Name {\
static constexpr const int id = Id;\
static constexpr const char* name = #Name;\
};
namespace Food {
ENUMITEM(1, Banana)
ENUMITEM(2, Apple)
ENUMITEM(3, Orange)
}
int main() {
cout << Food::Orange::id << ":" << Food::Orange::name << endl;
return 0;
}
输出:
3:Orange
== ==更新
使用:
#define STARTENUM() constexpr const int enumStart = __LINE__;
#define ENUMITEM(Name) \
struct Name {\
static constexpr const int id = __LINE__ - enumStart - 1;\
static constexpr const char* name = #Name;\
};
并使用一次ENUMITEM的第一次使用就不再被需要的ID之前。
namespace Food {
STARTENUM()
ENUMITEM(Banana)
ENUMITEM(Apple)
ENUMITEM(Orange)
}
变量enumStart
只有通过命名空间访问 - 所以仍然可以使用多个枚举。
您可以使用宏来解决这个问题:
#define MAKE_ENUM(name, ...) enum class name { __VA_ARGS__}; \
static std::vector<std::string> Enum_##name##_init(){\
const std::string content = #__VA_ARGS__; \
std::vector<std::string> str;\
size_t len = content.length();\
std::ostringstream temp;\
for(size_t i = 0; i < len; i ++) {\
if(isspace(content[i])) continue;\
else if(content[i] == ',') {\
str.push_back(temp.str());\
temp.str(std::string());}\
else temp<< content[i];}\
str.push_back(temp.str());\
return str;}\
static const std::vector<std::string> Enum_##name##_str_vec = Enum_##name##_init();\
static std::string to_string(name val){\
return Enum_##name##_str_vec[static_cast<size_t>(val)];\
}\
static std::string print_all_##name##_enum(){\
int count = 0;\
std::string ans;\
for(auto& item:Enum_##name##_str_vec)\
ans += std::to_string(count++) + ':' + item + '\n';\
return ans;\
}
由于静态变量只能被初始化一次,所以枚举_ ##名## _ str_vec将使用枚举_ ##名## _ init()函数本身首先初始化。
示例代码如下:
MAKE_ENUM(Analysis_Time_Type,
UNKNOWN,
REAL_TIME,
CLOSSING_TIME
);
然后你可以用下面的句子来打印一个枚举值:
to_string(Analysis_Time_Type::UNKNOWN)
并使用下面的句子打印所有枚举的字符串:
print_all_Analysis_Time_Type_enum()
如前所述,有做这种没有标准的方式。但有一点预处理魔术(类似于AlejoHausner的第二个贡献)和一些模板魔术,它可以非常优雅。
包括这一次代码:
#include <string>
#include <algorithm>
#define ENUM_VALS( name ) name,
#define ENUM_STRINGS( name ) # name,
/** Template function to return the enum value for a given string
* Note: assumes enums are all upper or all lowercase,
* that they are contiguous/default-ordered,
* and that the first value is the default
* @tparam ENUM type of the enum to retrieve
* @tparam ENUMSIZE number of elements in the enum (implicit; need not be passed in)
* @param valStr string version of enum value to convert; may be any capitalization (capitalization may be modified)
* @param enumStrs array of strings corresponding to enum values, assumed to all be in lower/upper case depending upon
* enumsUpper
* @param enumsUpper true if the enum values are in all uppercase, false if in all lowercase (mixed case not supported)
* @return enum value corresponding to valStr, or the first enum value if not found
*/
template <typename ENUM, size_t ENUMSIZE>
static inline ENUM fromString(std::string &valStr, const char *(&enumStrs)[ENUMSIZE], bool enumsUpper = true) {
ENUM e = static_cast< ENUM >(0); // by default, first value
// convert valStr to lower/upper-case
std::transform(valStr.begin(), valStr.end(), valStr.begin(), enumsUpper ? ::toupper : ::tolower);
for (size_t i = 0; i< ENUMSIZE; i++) {
if (valStr == std::string(enumStrs[i])) {
e = static_cast< ENUM >(i);
break;
}
}
return e;
}
然后定义每个枚举,如下所示:
//! Define ColorType enum with array for converting to/from strings
#define ColorTypes(ENUM) \
ENUM(BLACK) \
ENUM(RED) \
ENUM(GREEN) \
ENUM(BLUE)
enum ColorType {
ColorTypes(ENUM_VALS)
};
static const char* colorTypeNames[] = {
ColorTypes(ENUM_STRINGS)
};
你只需要一次枚举枚举值和代码来定义它是相当紧凑和直观。
价值必然在默认的方式进行编号(即0,1,2,...)。 fromString()
的代码假定枚举值是要么全部大写或小写(从字符串转换),默认值是第一个,当然你可以改变这些东西是如何处理的。
这里是你如何得到字符串值:
ColorType c = ColorType::BLUE;
std::cout << colorTypeNames[c]; // BLUE
这里是你如何设置一个字符串值,枚举:
ColorType c2 = fromString<ColorType>("Green", colorTypeNames); // == ColorType::GREEN