背景
在 C 中,枚举类型是一种非常常见的数据类型,它允许程序员定义一组命名的常量。然而,标准的 C 枚举在某些方面存在限制,比如无法直接将枚举值转换为字符串。这就导致枚举型变量对于书写日志着实不够友好,如果仅仅将枚举型变量对应的值输出,单纯的数值型变量可读性差,为此希望可以将枚举型变量对应的值输出为可以表达其真实含义的字符串。可以通过为枚举书写转换函数,将枚举值转换为可读性强的字符串,在书写日志时使用其转换后的字符串。
如下为spdlog中存在的log等级的枚举(有删减)
代码语言:javascript复制enum level_enum : int {
trace,
debug,
info,
warn,
err,
critical
};
为方便在日志中将日志等级以可读性强的字符串形式展示出来,提供如下的转换函数
代码语言:javascript复制std::string to_string(level_enum log_level)
{
auto log_level_str{""};
switch (log_level)
{
case trace:
{
log_level_str = "trace"; break;
}
case debug:
{
log_level_str = "debug"; break;
}
case info:
{
log_level_str = "info"; break;
}
case warn:
{
log_level_str = "warn"; break;
}
case err:
{
log_level_str = "error"; break;
}
case critical:
{
log_level_str = "critical"; break;
}
default:
break;
}
return log_level_str;
}
由如上代码可知,
- 需要为枚举中的每个数值匹配与其对应的字符串,即如果枚举中有50个数值,需要书写50个匹配项;
- 如果项目中有50个枚举,需要为50个枚举书写转换函数。
如果项目中存在多个枚举或者枚举中存在多个数值,那书写转换函数的工作量时非常大。这就需要今天的主角登场了——magic_enum
概述
为了解决如上问题,magic_enum提供了一种便捷的方式来处理枚举类型,让枚举变得更加灵活和易用。其可以实现
- 枚举值转换为字符串,字符串转换为对应的枚举值。
- 迭代枚举类型的所有可能值。
- 将枚举值转换为整数类型,整数类型转换为对应的枚举值。
- 在编译时生成枚举值的数量。
使用场景
可以依据magic_enum的功能并结合自己的实际需求,只要其已有的功能可以满足实际需求就可以应用。结合以往的经验,magic_enum常见的使用场景如下:
- 日志记录:将枚举值转换为字符串,方便记录日志并进行调试。
- 用户界面:在用户界面中显示枚举值的字符串形式,提高可读性。
- 配置文件:使用字符串表示枚举值,使得配置文件更加直观和易于理解。
- 状态机:将状态表示为枚举值,并根据不同的状态执行相应的操作。
使用方法
下载和编译
源码下载地址:
https://github.com/Neargye/magic_enum.git
或
https://github.com/Neargye/magic_enum.git
magic_enum为header only的库无需编译
如果需要查看工程中的examples时,需要使用cmake进行编译,关于这部分的资料已经很多了,在此不再赘述。
使用示例
代码语言:javascript复制#include"magic_enum.hpp"
enum level_enum : int {
trace,
debug,
info,
warn,
err,
critical
};
enum ObjectOperate
{
kCreate,
kPlay,
kPause,
kReplay,
kStop,
kDelete
};
void test_magic_enum()
{
level_enum level{ critical };
std::cout<<magic_enum::enum_name(level)<<"n";
ObjectOperate op{kCreate};
std::cout << magic_enum::enum_name(op) << "n";
}
/*
ouput:
critical
kCreate
*/
在使用magic_enum后无需再书写枚举值转字符串的函数,极大的提高了效率。
注意事项
magic_enum默认的支持的枚举值的取值范围为[-128,127],在该值范围以外的枚举值不能进行枚举值到字符串的转换。针对如上代码做出修改如下,
代码语言:javascript复制enum level_enum : int {
//同上
critical=255
};
enum ObjectOperate
{
kCreate= -129,
//同上
};
void test_magic_enum()
{
level_enum level{ critical };
std::cout<<magic_enum::enum_name(level)<<"n";
ObjectOperate op{kCreate};
std::cout << magic_enum::enum_name(op) << "n";
}
/*
ouput:
*/
以上输出为空,是因为需要被转换的枚举值均超出其范围,在枚举值不变的情况下,需增加结构体的书写,指定枚举的范围,即可实现枚举值的转换
代码语言:javascript复制enum level_enum : int {
//同上
critical=255
};
template <>
struct magic_enum::customize::enum_range<level_enum> {
//static constexpr int min = -128;
static constexpr int max = 512;
};
enum ObjectOperate
{
kCreate= -129,
//同上
};
template <>
struct magic_enum::customize::enum_range<ObjectOperate> {
static constexpr int min = -129;
//static constexpr int max = 127;
};
void test_magic_enum()
{
level_enum level{ critical };
std::cout<<magic_enum::enum_name(level)<<"n";
ObjectOperate op{kCreate};
std::cout << magic_enum::enum_name(op) << "n";
}
/*
ouput:
critical
kCreate
*/
如上两个结构体的声明实则实在修改可以枚举的范围。除了如上的书写方式,还可以在包含头文件前定义两个宏,形如
代码语言:javascript复制#define MAGIC_ENUM_RANGE_MIN -150
#define MAGIC_ENUM_RANGE_MAX 260
#include"magic_enum.hpp"
这会统一修改所有枚举可以支持的枚举范围。不建议使用这种粗粒度的方式修改枚举值的范围。尤其是在值过小/过大时,会增加编译时间。
总结
magic_enum是一个强大的工具,可以极大地简化在 C 中处理枚举类型的过程。通过提供简洁的 API 和丰富的功能,Magic Enum 为程序员提供了更多的灵活性和便利性,使得枚举类型的使用变得更加轻松和愉快。