概述
这里不介绍非侵入式的写法,具体想要了解的话可以观看这篇文章,这里仅介绍通过宏写法来进行转换的情况。
宏
这里拿minilog的log-level定义来介绍,首先肯定是需要按照等级先进行定义:
#define MINILOG_FOREACH_LOG_LEVEL(f) \
f(trace) f(debug) f(info) f(critical) f(warn) f(error) f(fatal)
后续如果要修改只用修改这一处就行了,优点是一劳永逸,不存在维护额外开销的问题,当然缺点也有,比如在写server的时候遇到过需要将传入的指令进行与操作整合的情况,这种类型的情况下就无法使用宏的方法,或者说需要额外处理。
在定义好各个level后就需要进行enum的声明了:
enum class log_level : std::uint8_t {
#define _FUNCTION(name) name,
MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTION
};
具体来说就是将log_level在enum中用宏函数进行了展开,避免无用代码的编写。接下来是文章的核心,怎么进行enum和string的相互转化。
inline std::string log_level_name(log_level level) {
switch (level) {
#define _FUNCTION(name) \
case log_level::name: \
return #name;
MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTION
}
return "unknown";
}
inline log_level log_level_from_name(std::string_view lev) {
#define _FUNCTION(name) \
if (lev == #name) return log_level::name;
MINILOG_FOREACH_LOG_LEVEL(_FUNCTION)
#undef _FUNCTION
return log_level::info;
}
非常方便,相当于自动维护对应的代码,事实上,clang 在定义TokenKind的时候,就是这么做的,具体的案例请参考。由于 clang 要适配多种语言前端,最后总计的TokenKind有几百个之多。如果不这样做,可想而知,进行Token的增加和修改会十分困难。