#include <array>
#include <string>
#include <utility>
#include <string_view>
template <typename E, E V>
constexpr auto PrettyName()
{
std::string_view name{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2};
name.remove_prefix(name.find_last_of(" ") + 1);
if (name.front() == '(') name.remove_prefix(name.size());
return name;
}
template <typename E, E V>
constexpr bool IsValidEnum()
{
return !PrettyName<E, V>().empty();
}
template <int... Seq>
constexpr auto MakeIntegerSequence(std::integer_sequence<int, Seq...>)
{
return std::integer_sequence<int, (Seq)...>();
}
constexpr auto NormalIntegerSequence = MakeIntegerSequence(std::make_integer_sequence<int, 32>());
template <typename E, int... Seq>
constexpr size_t GetEnumSize(std::integer_sequence<int, Seq...>)
{
constexpr std::array<bool, sizeof...(Seq)> valid{IsValidEnum<E, static_cast<E>(Seq)>()...};
constexpr std::size_t count = [](decltype((valid)) v) constexpr noexcept->std::size_t
{
auto cnt = std::size_t{0};
for (auto b : v) if (b) ++cnt;
return cnt;
}(valid);
return count;
}
template <typename E, int... Seq>
constexpr auto GetAllValidValues(std::integer_sequence<int, Seq...>)
{
constexpr std::size_t count = sizeof...(Seq);
constexpr std::array<bool, count> valid{IsValidEnum<E, static_cast<E>(Seq)>()...};
constexpr std::array<int, count> seq{Seq...};
std::array<int, GetEnumSize<E>(NormalIntegerSequence)> values{};
for (std::size_t i = 0, v = 0; i < count; ++i) if (valid[i]) values[v++] = seq[i];
return values;
}
template <typename E, int... Seq>
constexpr auto GetAllValidNames(std::integer_sequence<int, Seq...>)
{
constexpr std::array<std::string_view, sizeof...(Seq)> names{PrettyName<E, static_cast<E>(Seq)>()...};
std::array<std::string_view, GetEnumSize<E>(NormalIntegerSequence)> validNames{};
for (std::size_t i = 0, v = 0; i < names.size(); ++i) if (!names[i].empty()) validNames[v++] = names[i];
return validNames;
}
template <typename E>
constexpr std::string_view Enum2String(E V)
{
constexpr auto names = GetAllValidNames<E>(NormalIntegerSequence);
constexpr auto values = GetAllValidValues<E>(NormalIntegerSequence);
constexpr auto size = GetEnumSize<E>(NormalIntegerSequence);
for (size_t i = 0; i < size; ++i) if (static_cast<int>(V) == values[i]) return names[i];
return std::to_string(static_cast<int>(V));
}
这段代码是一套编译期枚举反射(Enum Reflection)工具,核心目标是在编译期实现枚举类型的 “值→字符串” 转换、枚举有效值统计、枚举名称提取等反射能力,无需运行时查表或手动维护字符串映射表。
C++ 原生不支持枚举的反射(无法直接通过枚举值获取其字面量名称),这套代码通过编译器内置宏(__PRETTY_FUNCTION__)和编译期计算(constexpr),在编译阶段解析枚举值的名称、统计有效枚举值数量、生成 “枚举值 - 名称” 映射,最终实现枚举值到字符串的高效转换。
template <typename E, E V>
constexpr auto PrettyName()
{
std::string_view name{__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__) - 2};
name.remove_prefix(name.find_last_of(" ") + 1);
if (name.front() == '(') name.remove_prefix(name.size());
return name;
}
- 核心依赖:
__PRETTY_FUNCTION__ 是编译器内置宏,会展开为当前函数的 “美化函数名”(包含模板参数)。
例如,实例化 PrettyName<Color, Color::Red>() 时,__PRETTY_FUNCTION__ 可能展开为:
constexpr auto PrettyName() [with E = Color; E V = Color::Red]。
- 逻辑:
- 截取
__PRETTY_FUNCTION__ 的有效部分(去掉末尾的空字符等);
- 从最后一个空格后截取字符串(提取枚举值的字面量,如
Color::Red);
- 处理异常情况(若截取结果以
( 开头,说明无有效名称,返回空)。
- 作用:编译期获取枚举值
V 的字面量名称(如 Color::Red)。
template <typename E, E V>
constexpr bool IsValidEnum()
{
return !PrettyName<E, V>().empty();
}
- 逻辑:若
PrettyName 返回非空字符串,说明 V 是枚举 E 的有效枚举值;否则为无效值(如手动强转的非法值)。
- 作用:筛选出枚举类型
E 的 “合法” 枚举值。
template <int... Seq>
constexpr auto MakeIntegerSequence(std::integer_sequence<int, Seq...>)
{
return std::integer_sequence<int, (Seq)...>();
}
constexpr auto NormalIntegerSequence = MakeIntegerSequence(std::make_integer_sequence<int, 32>());
- 逻辑:生成一个包含
0~31 的整数序列(std::integer_sequence<int, 0,1,2,...,31>)。
- 作用:枚举值通常是整数类型(默认从 0 开始),用这个序列遍历 “可能的枚举值范围”(这里限定 32 个,可扩展),用于后续筛选有效枚举值。
template <typename E, int... Seq>
constexpr size_t GetEnumSize(std::integer_sequence<int, Seq...>)
{
constexpr std::array<bool, sizeof...(Seq)> valid{IsValidEnum<E, static_cast<E>(Seq)>()...};
constexpr std::size_t count = [](decltype((valid)) v) constexpr noexcept->std::size_t
{
auto cnt = std::size_t{0};
for (auto b : v) if (b) ++cnt;
return cnt;
}(valid);
return count;
}
- 逻辑:
- 遍历
0~31 整数序列,逐个判断 static_cast<E>(Seq) 是否为有效枚举值(生成 valid 布尔数组);
- 编译期遍历
valid 数组,统计 true 的数量(即有效枚举值的个数)。
- 作用:获取枚举类型
E 的有效枚举值总数(如 Color 有 Red/Green/Blue 三个值,则返回 3)。
template <typename E, int... Seq>
constexpr auto GetAllValidValues(std::integer_sequence<int, Seq...>)
{
constexpr std::size_t count = sizeof...(Seq);
constexpr std::array<bool, count> valid{IsValidEnum<E, static_cast<E>(Seq)>()...};
constexpr std::array<int, count> seq{Seq...};
std::array<int, GetEnumSize<E>(NormalIntegerSequence)> values{};
for (std::size_t i = 0, v = 0; i < count; ++i) if (valid[i]) values[v++] = seq[i];
return values;
}
- 逻辑:
- 遍历
0~31 整数序列,筛选出其中 “有效枚举值” 对应的整数;
- 将这些整数存入编译期数组返回。
- 作用:生成枚举类型
E 的所有有效枚举值的整数值数组(如 Color 对应 {0,1,2})。
template <typename E, int... Seq>
constexpr auto GetAllValidNames(std::integer_sequence<int, Seq...>)
{
constexpr std::array<std::string_view, sizeof...(Seq)> names{PrettyName<E, static_cast<E>(Seq)>()...};
std::array<std::string_view, GetEnumSize<E>(NormalIntegerSequence)> validNames{};
for (std::size_t i = 0, v = 0; i < names.size(); ++i) if (!names[i].empty()) validNames[v++] = names[i];
return validNames;
}
- 逻辑:
- 遍历
0~31 整数序列,为每个值生成对应的枚举名称(PrettyName);
- 筛选出非空的名称,存入编译期数组返回。
- 作用:生成枚举类型
E 的所有有效枚举值的名称数组(如 Color 对应 {"Color::Red", "Color::Green", "Color::Blue"})。
- 逻辑:
- 编译期预生成枚举
E 的 “值 - 名称” 映射数组(values 和 names);
- 运行时遍历映射数组,找到输入枚举值
V 对应的名称并返回;
- 若未找到(非法枚举值),返回其整数值的字符串(如
5)。
- 作用:对外提供统一的枚举值转字符串接口,编译期完成映射表构建,运行时仅需查表,效率极高。
- 编译期计算:所有映射表(值、名称、数量)均在编译期生成,无运行时开销;
- 无侵入性:无需修改原有枚举定义,仅需模板实例化即可使用;
- 局限性:
- 依赖编译器的
__PRETTY_FUNCTION__ 格式(不同编译器可能需要适配);
- 仅支持枚举值为
0~31 的场景(可通过调整 std::make_integer_sequence<int, 32> 的参数扩展);
- 仅支持 “未指定底层类型 / 底层类型为 int” 的枚举(需适配其他底层类型需修改整数序列类型)。
总结:这套代码是 C++ 中 “无运行时开销的枚举反射” 的典型实现,通过编译器宏和编译期模板计算,填补了 C++ 原生不支持枚举反射的短板,核心价值是自动、高效地实现枚举值到字面量名称的转换。