与其他类似的问题不同,这个问题是关于如何使用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++代码……
对于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或下一个标准的统一静态反射提案。
我写了一个库来解决这个问题,所有的事情都发生在编译时,除了获取消息。
用法:
使用宏DEF_MSG定义宏和消息对:
DEF_MSG(CODE_OK, "OK!")
DEF_MSG(CODE_FAIL, "Fail!")
CODE_OK是要使用的宏,“OK!”是相应的消息。
使用get_message()或gm()来获取消息:
get_message(CODE_FAIL); // will return "Fail!"
gm(CODE_FAIL); // works exactly the same as above
使用MSG_NUM查找已经定义了多少个宏。它会自动增加,你不需要做任何事情。
预定义的消息:
MSG_OK: OK
MSG_BOTTOM: Message bottom
项目:libcodemsg
标准库不会创建额外的数据。一切都发生在编译时。在message_def.h中,它生成一个名为MSG_CODE的enum;在message_def.c中,它生成一个变量,保存静态const char* _g_messages[]中的所有字符串。
在这种情况下,库只能创建一个枚举。这对于返回值非常理想,例如:
MSG_CODE foo(void) {
return MSG_OK; // or something else
}
MSG_CODE ret = foo();
if (MSG_OK != ret) {
printf("%s\n", gm(ret););
}
我喜欢这种设计的另一个原因是,您可以在不同的文件中管理消息定义。
我发现这个问题的解看起来好多了。
这和尤里·芬克尔斯坦的观点相似;但不需要提高。我正在使用一个地图,所以你可以分配任何值枚举,任何顺序。
枚举类的声明为:
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;
}
您可以在这里运行代码
很长一段时间以来,我也一直为这个问题感到沮丧,还有以适当的方式将类型转换为字符串的问题。然而,对于最后一个问题,我对在标准c++中打印变量类型是否可能解释的解决方案感到惊讶?,使用的思想从Can I obtain c++ type name in a constexpr way?使用这种技术,可以构造一个类似的函数来获取枚举值为string:
#include <iostream>
using namespace std;
class static_string
{
const char* const p_;
const std::size_t sz_;
public:
typedef const char* const_iterator;
template <std::size_t N>
constexpr static_string(const char(&a)[N]) noexcept
: p_(a)
, sz_(N - 1)
{}
constexpr static_string(const char* p, std::size_t N) noexcept
: p_(p)
, sz_(N)
{}
constexpr const char* data() const noexcept { return p_; }
constexpr std::size_t size() const noexcept { return sz_; }
constexpr const_iterator begin() const noexcept { return p_; }
constexpr const_iterator end() const noexcept { return p_ + sz_; }
constexpr char operator[](std::size_t n) const
{
return n < sz_ ? p_[n] : throw std::out_of_range("static_string");
}
};
inline std::ostream& operator<<(std::ostream& os, static_string const& s)
{
return os.write(s.data(), s.size());
}
/// \brief Get the name of a type
template <class T>
static_string typeName()
{
#ifdef __clang__
static_string p = __PRETTY_FUNCTION__;
return static_string(p.data() + 30, p.size() - 30 - 1);
#elif defined(_MSC_VER)
static_string p = __FUNCSIG__;
return static_string(p.data() + 37, p.size() - 37 - 7);
#endif
}
namespace details
{
template <class Enum>
struct EnumWrapper
{
template < Enum enu >
static static_string name()
{
#ifdef __clang__
static_string p = __PRETTY_FUNCTION__;
static_string enumType = typeName<Enum>();
return static_string(p.data() + 73 + enumType.size(), p.size() - 73 - enumType.size() - 1);
#elif defined(_MSC_VER)
static_string p = __FUNCSIG__;
static_string enumType = typeName<Enum>();
return static_string(p.data() + 57 + enumType.size(), p.size() - 57 - enumType.size() - 7);
#endif
}
};
}
/// \brief Get the name of an enum value
template <typename Enum, Enum enu>
static_string enumName()
{
return details::EnumWrapper<Enum>::template name<enu>();
}
enum class Color
{
Blue = 0,
Yellow = 1
};
int main()
{
std::cout << "_" << typeName<Color>() << "_" << std::endl;
std::cout << "_" << enumName<Color, Color::Blue>() << "_" << std::endl;
return 0;
}
上面的代码只在Clang(参见https://ideone.com/je5Quv)和VS2015上进行了测试,但是应该可以通过对整数常量进行一些调整来适应其他编译器。当然,它仍然在底层使用宏,但至少有一个宏不需要访问枚举实现。