与其他类似的问题不同,这个问题是关于如何使用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++代码……
我采用了@antron的想法,并以不同的方式实现:生成一个真正的枚举类。
这个实现满足了最初问题中列出的所有要求,但目前只有一个真正的限制:它假设枚举值要么没有提供,要么如果提供了,必须从0开始,并且无间隙地按顺序递增。
这并不是一个内在的限制——只是我不使用特别的enum值。如果需要,可以用传统的开关/案例实现替换向量查找。
解决方案使用一些c++17作为内联变量,但如果需要,这可以很容易地避免。因为简单,它还使用boost:trim。
最重要的是,它只需要30行代码,而且没有黑魔法宏。
代码如下。它的意思是放在头和包括在多个编译模块。
它可以使用与本文前面建议的相同的方式:
ENUM(Channel, int, Red, Green = 1, Blue)
std::out << "My name is " << Channel::Green;
//prints My name is Green
请让我知道这是有用的,以及如何进一步改进。
#include <boost/algorithm/string.hpp>
struct EnumSupportBase {
static std::vector<std::string> split(const std::string s, char delim) {
std::stringstream ss(s);
std::string item;
std::vector<std::string> tokens;
while (std::getline(ss, item, delim)) {
auto pos = item.find_first_of ('=');
if (pos != std::string::npos)
item.erase (pos);
boost::trim (item);
tokens.push_back(item);
}
return tokens;
}
};
#define ENUM(EnumName, Underlying, ...) \
enum class EnumName : Underlying { __VA_ARGS__, _count }; \
struct EnumName ## Support : EnumSupportBase { \
static inline std::vector<std::string> _token_names = split(#__VA_ARGS__, ','); \
static constexpr const char* get_name(EnumName enum_value) { \
int index = (int)enum_value; \
if (index >= (int)EnumName::_count || index < 0) \
return "???"; \
else \
return _token_names[index].c_str(); \
} \
}; \
inline std::ostream& operator<<(std::ostream& os, const EnumName & es) { \
return os << EnumName##Support::get_name(es); \
}
非常简单的解决方案,但有一个很大的限制:你不能将自定义值分配给枚举值,但通过正确的正则表达式,你可以这样做。你也可以添加一个映射,将它们转换回枚举值,而不需要更多的努力:
#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);
Magic Enum头库为c++ 17的枚举(到字符串,从字符串,迭代)提供静态反射。
#include <magic_enum.hpp>
enum Color { RED = 2, BLUE = 4, GREEN = 8 };
Color color = Color::RED;
auto color_name = magic_enum::enum_name(color);
// color_name -> "RED"
std::string color_name{"GREEN"};
auto color = magic_enum::enum_cast<Color>(color_name)
if (color.has_value()) {
// color.value() -> Color::GREEN
};
更多示例请查看home repository https://github.com/Neargye/magic_enum。
缺点在哪里?
这个库使用了一个特定于编译器的hack(基于__PRETTY_FUNCTION__ / __FUNCSIG__),它在Clang >= 5, MSVC >= 15.3和GCC >= 9上工作。
枚举值必须在范围[MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX]内。
By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128.
If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX.
MAGIC_ENUM_RANGE_MIN must be less or equals than 0 and must be greater than INT16_MIN.
MAGIC_ENUM_RANGE_MAX must be greater than 0 and must be less than INT16_MAX.
If need another range for specific enum type, add specialization enum_range for necessary enum type.
#include <magic_enum.hpp>
enum number { one = 100, two = 200, three = 300 };
namespace magic_enum {
template <>
struct enum_range<number> {
static constexpr int min = 100;
static constexpr int max = 300;
};
}
很长一段时间以来,我也一直为这个问题感到沮丧,还有以适当的方式将类型转换为字符串的问题。然而,对于最后一个问题,我对在标准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上进行了测试,但是应该可以通过对整数常量进行一些调整来适应其他编译器。当然,它仍然在底层使用宏,但至少有一个宏不需要访问枚举实现。