用c++遍历一个枚举值的名字,
enum MyEnum { kRed = 0, kGreen = 1, kBlue = 15 }; int main() { print<MyEnum>(); } 要求输出: kRed = 0 kGreen = 1 kBlue = 15
怎么实现呢?
第一步:我们写个函数验证下
template<typename Enum, Enum enumValue> void test() { printf("%s
", __PRETTY_FUNCTION__); } int main() { test<MyEnum, (MyEnum)15>(); }
可以看到,上面的代码输出了
void test() [with Enum = MyEnum; Enum enumValue = kBlue]
由此,我们通过 15,获取打出了结果中的 "kBlue",这是我们需要的。
这段代码用了 __PRETTY_FUNCTION__ 黑魔法。
第二步:遍历枚举中的值
上一步需要输入15这个值,下面我们用程序猜想枚举类中含有哪些整数,我们预设在一个范围 (A-B)内猜想。为此,我们可以构造一个A-B的模板参数序列,
template<int value> int print_int() { printf("%d ", value); //test<Enum, (Enum)value>(); //加上此行试试 return 0; } template<int A, int... value> void seq2(const std::integer_sequence<int, value...> &) { auto l = {print_int<A + value>()...}; } template<int A, int B> void seq1() { seq2<A>(std::make_integer_sequence<int, B - A>()); } int main() { seq1<-5, 3>(); }
这段代码通过指定范围 (-5, 3),将范围内的整数传给模板参数
-5 -4 -3 -2 -1 0 1 2
这段代码用了几个小魔法:
到了这里,问题的答案呼之欲出了,将print_int函数中的内容改成
test<Enum, (Enum)value>();
我们看到,程序输出
void test() [with Enum = MyEnum; Enum enumValue = (MyEnum)4294967291] void test() [with Enum = MyEnum; Enum enumValue = (MyEnum)4294967292] void test() [with Enum = MyEnum; Enum enumValue = (MyEnum)4294967293] void test() [with Enum = MyEnum; Enum enumValue = (MyEnum)4294967294] void test() [with Enum = MyEnum; Enum enumValue = (MyEnum)4294967295] void test() [with Enum = MyEnum; Enum enumValue = kRed] void test() [with Enum = MyEnum; Enum enumValue = kGreen] void test() [with Enum = MyEnum; Enum enumValue = (MyEnum)2]
第三步:去掉不要的值
第二步输出,还多了一些不需要的值,我们做一些修整,就能达到开始题目的目标。
同时还需要把数值的猜测范围再扩大一点,以下扩大到-64到64,以及所有2的n次方的值。
修正细节不说了,完整代码贴这里
#include <map> #include <string> #include <utility> #include <stdio.h> struct DummyFlag {}; template<typename Enum, typename T, Enum enumValue> inline int get_enum_value(std::map<int, std::string> &values) { #if defined _MSC_VER && !defined __clang__ std::string func(__FUNCSIG__); std::string mark = "DummyFlag"; auto pos = func.find(mark) + mark.size(); std::string enumStr = func.substr(pos); auto start = enumStr.find_first_not_of(", "); auto end = enumStr.find('>'); if (start != enumStr.npos && end != enumStr.npos && enumStr[start] != '(') { enumStr = enumStr.substr(start, end - start); values.insert({ (int)enumValue, enumStr }); } #else // gcc, clang std::string func(__PRETTY_FUNCTION__); std::string mark = "enumValue = "; auto pos = func.find(mark) + mark.size(); std::string enumStr = func.substr(pos, func.size() - pos - 1); char ch = enumStr[0]; if(!(ch >= '0' && ch <= '9') && ch != '(') values.insert({(int)enumValue, enumStr}); #endif return 0; } template <typename Enum, int min_value, int... ints> void guess_enum_range(std::map<int, std::string> &values, const std::integer_sequence<int, ints...> &) { auto dummy = { get_enum_value<Enum, DummyFlag, (Enum)(ints + min_value)>(values)... }; } template <typename Enum, int... ints> void guess_enum_bit_range(std::map<int, std::string> &values, const std::integer_sequence<int, ints...> &) { auto dummy = { get_enum_value<Enum, DummyFlag, (Enum)0>(values), get_enum_value<Enum, DummyFlag, (Enum)(1 << (int)ints)>(values)... }; } template<typename Enum, int min_value = -64, int max_value = 64> void print() { std::map<int, std::string> values; guess_enum_range<Enum, min_value>(values, std::make_integer_sequence<int, max_value - min_value>()); guess_enum_bit_range<Enum>(values, std::make_integer_sequence<int, 32>()); for(const auto &value: values) { printf("%s = %d
", value.second.c_str(), value.first); } } enum MyEnum { kRed = 0, kGreen = 1, kBlue = 15, }; enum MyBitEnum { kTom = 0x01, kJerry = 0x02, kZhangSan = 0x400, kXiaoMing = 0x800, }; int main() { print<MyEnum>(); print<MyBitEnum>(); }
最后我们看到,两个枚举的字符串和值,均顺利输出如下
kRed = 0 kGreen = 1 kBlue = 15 kTom = 1 kJerry = 2 kZhangSan = 1024 kXiaoMing = 2048
以上gcc 9, clang 13 和 vs2019 编译通过
注意: