[杂项] Enum to String
预备知识
- Standard predefined identifier: 预定义标识符
__func__
: 返回所在函数闭包对应的函数名. 本质上是常量字符数组, 由编译器提供(ISO C99 & ISO C++11), 格式:const char __func__[]
- Standard predefined macro: 预定义宏
MSVC supports the predefined preprocessor macros required by the ANSI/ISO C99, C11, and C17 standards, and the ISO C++14, C++17, and C++20 standards.
- 可提供通用的辅助性信息
__LINE__
: 提供当前函数所在的行号, 格式: 数字__FILE__
: 当前函数所在的文件, 格式: 字符串__DATE__
: 当前日期, 格式:Jan 19 2023
__TIME__
: 当前时间信息, 格式:22:35:41
- 提供具体到函数调用的信息本文将利用
__FUNCSIG__
提供的信息, 模拟反射机制来将 enum 变量转为字符串__FUNCTION__
: 提供所在闭包对应函数的函数名, 格式: 返回__func__
的值__FUNCDNAME__
: 提供函数修饰名, 格式:?main@@YAHXZ
或main
或_main
(可理解为符号表中导出的符号)__FUNCSIG__
: 提供函数的完整声明(同g++支持的宏:__PRETTY_FUNCTION__
), 格式:int __cdecl main(void)
- 可提供通用的辅助性信息
- 编译方式: 导出符号的修饰规则, 会影响到部分预定义宏的返回结果
extern "C"
: 不带修饰符. 此时,__FUNCTION__
与__FUNCDNAME__
的值相同- 其他: 导出符号被修饰, 如:
?main@@YAHXZ
, 可拆分为?
+main
+@@
+参数表代号
- 修饰符: 用于函数重载
- 代号: XDEFHIJKMN_NU, 比如: @@YAHXZ可拆分为
- @@YA:起始位置, 也有@@YG, @@YI
- H: 返回类型为
int
- X: 参数列表为空, 即
void
- Z: 结束位置, 也有@Z
- 调用约定:
_cdecl
: C Declaration 的缩写, 即采用 C 语言的声明方式(C缺省调用方式)- 参数从右—>左依次入栈
- 手动清栈, 即由调用者来恢复堆栈
_stdcall
: Standard Call 的缩写, 是 C++ 的标准调用方式. MSVC 宏有PASCAL
,WINAPI
,CALLBACK
- 参数从右—>左依次入栈
- 自动清栈, 即由被调用函数来恢复堆栈
_fastcall
- 参数借由CPU寄存器(ecx, edx)和堆栈来处理参数信息, 从右—>左依次入栈
- 自动清栈: 同
_stdcall
, 减轻调用者负担
简介
业务开发过程中, 会遇到需要获取
- 配置信息: 枚举值本身在代码逻辑中用于归类; 枚举名在初始化阶段作为配置信息的索引
- 多语言: 完整的枚举名列表用于前端UI展示; 枚举值用于用户交互进行语言切换
本文目标是模拟反射机制(c#, java), 获取对应枚举的相关信息. 由预备知识可知, __FUNCSIG__
可获取当前函数的完整声明信息(包括调用约定信息). 不由产生两个疑问:
__FUNCSIG__
是如何获得函数的完整声明?- 函数的声明如何能获得任意类型的
enum class
的相关信息?
好的问题远比答案重要: 在编译阶段, 模板函数会对所有的枚举类型进行实例化. 而实例化过程中, 传入的枚举类型及枚举值就是我们所需要的相关信息. 本文目标是提供操作枚举类型的工具函数, 如下:
get_enum_name
: 通过枚举值, 获取对应名称get_all_enum_names
: 获取枚举类型中所有值对应的名称列表get_enum_value
: 通过名称, 获取对应枚举值check_enum_valid
: 确认是否是有效的枚举(value or name)
准备阶段
考虑到兼顾不同编译器的情况, 需要对不同编译器提供的预定义宏进行适配. 个人比较喜欢 __PRETTY_FUNCTION__
, 如下:
msvc:
__FUNCSIG__
Defined as a string literal that contains the signature of the enclosing function.gcc: In C,
__PRETTY_FUNCTION__
is yet another name for__func__
, except that at file scope (or, in C++, namespace scope), it evaluates to the string “top level”. In addition, in C++,__PRETTY_FUNCTION__
contains the signature of the function as well as its bare name.
|
同时, 提供工具函数用于遍历枚举类的所有枚举值: 根据阈值 boundary
, 生成枚举值所在范围列表 [-boundary, boundary)
|
代码实现
get_enum_name
: 由于std::regex
不支持 look behind, 此处藉由QRegualr
实现对编译期常量V
和类型E
的捕获.
|
get_all_enum_names
:
|
接口封装
|