百科问答小站 logo
百科问答小站 font logo



C++代码可以魔法到什么程度? 第1页

  

user avatar   xhawk18 网友的相关建议: 
      

用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     

这段代码用了几个小魔法:

  1. make_integer_sequence,将一个整数扩展为可变参数的列表(见seq1到seq2函数的变化)
  2. auto l 加花括号初始化,调用了不限个数的 print_int 函数。

到了这里,问题的答案呼之欲出了,将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 编译通过

注意:

  1. gcc 9之前的版本不好使
  2. 如果用VC,需要把 __PRETTY_FUNCTION__ 改成 __FUNCSIG__,同时 get_enum_value 解析函数要根据 __FUNCSIG__ 的特性做修改。。。



  

相关话题

  C++笔试算法题允许用stl函数吗? 
  C++为何不允许在函数中直接传递数组? 
  C/C++编译器在什么情况下不会重排语句? 
  有哪些轻量级适合阅读的优秀 C++ 开源项目? 
  该如何优雅地黑 C++? 
  C/C++中,设计的时候字符/字符串为什么要加引号? 
  怎么在 Windows 7 下用 C++,不兼容啊? 
  c++中 为什么数组不能被vector完全替代? 
  Qt 为什么在桌面应用(Windows 平台)中不流行呢? 
  关于阿里巴巴2011年一道C++笔试题(struct和class)的疑问? 

前一个讨论
如果把赵敏放到华筝的位置上,她可能争得到郭靖吗?
下一个讨论
如果原神策划给你一个机会,能将一个四星角色升级成五星的资质,你们会选择哪个四星?排除班尼特,行秋。?





© 2024-06-08 - tinynew.org. All Rights Reserved.
© 2024-06-08 - tinynew.org. 保留所有权利