与其他类似的问题不同,这个问题是关于如何使用c++的新特性。
2008 c Is there a simple way to convert C++ enum to string?
2008 c Easy way to use variables of enum types as string in C?
2008 c++ How to easily map c++ enums to strings
2008 c++ Making something both a C identifier and a string?
2008 c++ Is there a simple script to convert C++ enum to string?
2009 c++ How to use enums as flags in C++?
2011 c++ How to convert an enum type variable to a string?
2011 c++ Enum to String C++
2011 c++ How to convert an enum type variable to a string?
2012 c How to convert enum names to string in c
2013 c Stringifying an conditionally compiled enum in C
看了很多答案后,我还没有找到:
优雅的方式使用c++ 11、c++ 14或c++ 17的新特性
或者在Boost中使用一些现成的东西
还有一些东西计划在c++ 20中实现
例子
举例往往比冗长的解释更好。
您可以在Coliru上编译和运行这个代码片段。
(另一个前面的例子也可用)
#include <map>
#include <iostream>
struct MyClass
{
enum class MyEnum : char {
AAA = -8,
BBB = '8',
CCC = AAA + BBB
};
};
// Replace magic() by some faster compile-time generated code
// (you're allowed to replace the return type with std::string
// if that's easier for you)
const char* magic (MyClass::MyEnum e)
{
const std::map<MyClass::MyEnum,const char*> MyEnumStrings {
{ MyClass::MyEnum::AAA, "MyClass::MyEnum::AAA" },
{ MyClass::MyEnum::BBB, "MyClass::MyEnum::BBB" },
{ MyClass::MyEnum::CCC, "MyClass::MyEnum::CCC" }
};
auto it = MyEnumStrings.find(e);
return it == MyEnumStrings.end() ? "Out of range" : it->second;
}
int main()
{
std::cout << magic(MyClass::MyEnum::AAA) <<'\n';
std::cout << magic(MyClass::MyEnum::BBB) <<'\n';
std::cout << magic(MyClass::MyEnum::CCC) <<'\n';
}
约束
请不要无价值的重复其他答案或基本链接。
请避免基于宏的臃肿答案,或尽量减少#define开销。
请不要手动enum ->字符串映射。
很高兴有
支持从不同于零的数字开始的enum值
支持负enum值
支持碎片enum值
支持类枚举(c++ 11)
支持类枚举:<类型>有任何允许的<类型> (c++ 11)
编译时(不是运行时)到字符串的转换,
或者至少在运行时快速执行(例如std::map不是一个好主意…)
constexpr (c++ 11,然后在c++ 14/17/20中放松)
noexcept (C + + 11)
c++ 17/ c++ 20友好的代码片段
一个可能的想法是使用c++编译器功能,在编译时使用基于可变参数模板类和constexpr函数的元编程技巧来生成c++代码……
非常简单的解决方案,但有一个很大的限制:你不能将自定义值分配给枚举值,但通过正确的正则表达式,你可以这样做。你也可以添加一个映射,将它们转换回枚举值,而不需要更多的努力:
#include <vector>
#include <string>
#include <regex>
#include <iterator>
std::vector<std::string> split(const std::string& s,
const std::regex& delim = std::regex(",\\s*"))
{
using namespace std;
vector<string> cont;
copy(regex_token_iterator<string::const_iterator>(s.begin(), s.end(), delim, -1),
regex_token_iterator<string::const_iterator>(),
back_inserter(cont));
return cont;
}
#define EnumType(Type, ...) enum class Type { __VA_ARGS__ }
#define EnumStrings(Type, ...) static const std::vector<std::string> \
Type##Strings = split(#__VA_ARGS__);
#define EnumToString(Type, ...) EnumType(Type, __VA_ARGS__); \
EnumStrings(Type, __VA_ARGS__)
使用的例子:
EnumToString(MyEnum, Red, Green, Blue);
这和尤里·芬克尔斯坦的观点相似;但不需要提高。我正在使用一个地图,所以你可以分配任何值枚举,任何顺序。
枚举类的声明为:
DECLARE_ENUM_WITH_TYPE(TestEnumClass, int32_t, ZERO = 0x00, TWO = 0x02, ONE = 0x01, THREE = 0x03, FOUR);
下面的代码将自动创建枚举类并重载:
'+' '+='用于std::string
'<<'用于流
'~'只是转换为字符串(任何一元运算符都可以,但我个人不喜欢它的清晰度)
'*'获取枚举的计数
不需要boost,提供所有需要的功能。
代码:
#include <algorithm>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>
#define STRING_REMOVE_CHAR(str, ch) str.erase(std::remove(str.begin(), str.end(), ch), str.end())
std::vector<std::string> splitString(std::string str, char sep = ',') {
std::vector<std::string> vecString;
std::string item;
std::stringstream stringStream(str);
while (std::getline(stringStream, item, sep))
{
vecString.push_back(item);
}
return vecString;
}
#define DECLARE_ENUM_WITH_TYPE(E, T, ...) \
enum class E : T \
{ \
__VA_ARGS__ \
}; \
std::map<T, std::string> E##MapName(generateEnumMap<T>(#__VA_ARGS__)); \
std::ostream &operator<<(std::ostream &os, E enumTmp) \
{ \
os << E##MapName[static_cast<T>(enumTmp)]; \
return os; \
} \
size_t operator*(E enumTmp) { (void) enumTmp; return E##MapName.size(); } \
std::string operator~(E enumTmp) { return E##MapName[static_cast<T>(enumTmp)]; } \
std::string operator+(std::string &&str, E enumTmp) { return str + E##MapName[static_cast<T>(enumTmp)]; } \
std::string operator+(E enumTmp, std::string &&str) { return E##MapName[static_cast<T>(enumTmp)] + str; } \
std::string &operator+=(std::string &str, E enumTmp) \
{ \
str += E##MapName[static_cast<T>(enumTmp)]; \
return str; \
} \
E operator++(E &enumTmp) \
{ \
auto iter = E##MapName.find(static_cast<T>(enumTmp)); \
if (iter == E##MapName.end() || std::next(iter) == E##MapName.end()) \
iter = E##MapName.begin(); \
else \
{ \
++iter; \
} \
enumTmp = static_cast<E>(iter->first); \
return enumTmp; \
} \
bool valid##E(T value) { return (E##MapName.find(value) != E##MapName.end()); }
#define DECLARE_ENUM(E, ...) DECLARE_ENUM_WITH_TYPE(E, int32_t, __VA_ARGS__)
template <typename T>
std::map<T, std::string> generateEnumMap(std::string strMap)
{
STRING_REMOVE_CHAR(strMap, ' ');
STRING_REMOVE_CHAR(strMap, '(');
std::vector<std::string> enumTokens(splitString(strMap));
std::map<T, std::string> retMap;
T inxMap;
inxMap = 0;
for (auto iter = enumTokens.begin(); iter != enumTokens.end(); ++iter)
{
// Token: [EnumName | EnumName=EnumValue]
std::string enumName;
T enumValue;
if (iter->find('=') == std::string::npos)
{
enumName = *iter;
}
else
{
std::vector<std::string> enumNameValue(splitString(*iter, '='));
enumName = enumNameValue[0];
//inxMap = static_cast<T>(enumNameValue[1]);
if (std::is_unsigned<T>::value)
{
inxMap = static_cast<T>(std::stoull(enumNameValue[1], 0, 0));
}
else
{
inxMap = static_cast<T>(std::stoll(enumNameValue[1], 0, 0));
}
}
retMap[inxMap++] = enumName;
}
return retMap;
}
例子:
DECLARE_ENUM_WITH_TYPE(TestEnumClass, int32_t, ZERO = 0x00, TWO = 0x02, ONE = 0x01, THREE = 0x03, FOUR);
int main(void) {
TestEnumClass first, second;
first = TestEnumClass::FOUR;
second = TestEnumClass::TWO;
std::cout << first << "(" << static_cast<uint32_t>(first) << ")" << std::endl; // FOUR(4)
std::string strOne;
strOne = ~first;
std::cout << strOne << std::endl; // FOUR
std::string strTwo;
strTwo = ("Enum-" + second) + (TestEnumClass::THREE + "-test");
std::cout << strTwo << std::endl; // Enum-TWOTHREE-test
std::string strThree("TestEnumClass: ");
strThree += second;
std::cout << strThree << std::endl; // TestEnumClass: TWO
std::cout << "Enum count=" << *first << std::endl;
}
您可以在这里运行代码
嗯,还有另一个选择。一个典型的用例是,您需要为HTTP谓词使用常量,并使用其字符串版本值。
示例:
int main () {
VERB a = VERB::GET;
VERB b = VERB::GET;
VERB c = VERB::POST;
VERB d = VERB::PUT;
VERB e = VERB::DELETE;
std::cout << a.toString() << std::endl;
std::cout << a << std::endl;
if ( a == VERB::GET ) {
std::cout << "yes" << std::endl;
}
if ( a == b ) {
std::cout << "yes" << std::endl;
}
if ( a != c ) {
std::cout << "no" << std::endl;
}
}
VERB类:
// -----------------------------------------------------------
// -----------------------------------------------------------
class VERB {
private:
// private constants
enum Verb {GET_=0, POST_, PUT_, DELETE_};
// private string values
static const std::string theStrings[];
// private value
const Verb value;
const std::string text;
// private constructor
VERB (Verb v) :
value(v), text (theStrings[v])
{
// std::cout << " constructor \n";
}
public:
operator const char * () const { return text.c_str(); }
operator const std::string () const { return text; }
const std::string toString () const { return text; }
bool operator == (const VERB & other) const { return (*this).value == other.value; }
bool operator != (const VERB & other) const { return ! ( (*this) == other); }
// ---
static const VERB GET;
static const VERB POST;
static const VERB PUT;
static const VERB DELETE;
};
const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"};
const VERB VERB::GET = VERB ( VERB::Verb::GET_ );
const VERB VERB::POST = VERB ( VERB::Verb::POST_ );
const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ );
const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ );
// end of file
下面的解决方案是基于给定enum的std::array<std::string,N>。
对于将enum转换为std::string,我们只需将enum转换为size_t,然后从数组中查找字符串。该操作是O(1),不需要堆分配。
#include <boost/preprocessor/seq/transform.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <string>
#include <array>
#include <iostream>
#define STRINGIZE(s, data, elem) BOOST_PP_STRINGIZE(elem)
// ENUM
// ============================================================================
#define ENUM(X, SEQ) \
struct X { \
enum Enum {BOOST_PP_SEQ_ENUM(SEQ)}; \
static const std::array<std::string,BOOST_PP_SEQ_SIZE(SEQ)> array_of_strings() { \
return {{BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(STRINGIZE, 0, SEQ))}}; \
} \
static std::string to_string(Enum e) { \
auto a = array_of_strings(); \
return a[static_cast<size_t>(e)]; \
} \
}
对于std::string到enum的转换,我们必须对数组进行线性搜索,并将数组索引强制转换为enum。
这里有一些用法示例:http://coliru.stacked-crooked.com/a/e4212f93bee65076
编辑:重做我的解决方案,以便自定义Enum可以在类中使用。
对于c++ 17 c++ 20,您将对反思研究小组(SG7)的工作感兴趣。还有一系列平行的论文,包括措辞(P0194)和基本原理、设计和进化(P0385)。(链接解析为每个系列的最新论文。)
从P0194r2(2016-10-15)开始,该语法将使用建议的reflexpr关键字:
meta::get_base_name_v<
meta::get_element_m<
meta::get_enumerators_m<reflexpr(MyEnum)>,
0>
>
例如(改编自Matus Choclik的reflexpr clang分支):
#include <reflexpr>
#include <iostream>
enum MyEnum { AAA = 1, BBB, CCC = 99 };
int main()
{
auto name_of_MyEnum_0 =
std::meta::get_base_name_v<
std::meta::get_element_m<
std::meta::get_enumerators_m<reflexpr(MyEnum)>,
0>
>;
// prints "AAA"
std::cout << name_of_MyEnum_0 << std::endl;
}
静态反射未能进入c++ 17(更确切地说,进入了2016年11月在Issaquah举行的标准会议上提出的可能是最终草案),但有信心它将进入c++ 20;摘自赫布·萨特的旅行报告:
特别是,反射研究小组审查了最新合并的静态反射提案,并发现它准备在我们的下一次会议上进入主要的进化小组,开始考虑TS或下一个标准的统一静态反射提案。