分类: C/C++
2020-07-13 16:07:50
你想要的功能全都有
特性预览
编写过程中有进行参考( tref,refl-cpp)
C++ 没有反射,猴年马月就有了
在猴年马月到来之前,我们需要自己实现反射
这里讨论静态反射库
开源社区上有一些反射库,如refl-cpp,我觉得它们写得很差
因此我写了一个,比它们都强,而且只要 99 行(refl-cpp 是 4500 行左右)
你可能觉得,99 行能有啥功能?实际上它拥有了静态反射的所有核心部分
静态就只能做这么多了,感觉表达能力上已经是静态的极限了,再多,那就是动态反射了
源码链接:USRefl_99.h
除了 stl 没有任何其他依赖,没有任何宏,拿了直接用
99 行是压缩过的(省略了许多换行),原版本为 USRefl.h
【警告】
C++ 功底比较一般的读者,库的源码和设计部分可以跳过,直接看第 3 节的示例部分,看看库用起来是什么“姿势“
【其他】
如果上了 C++20,consteval 功能可以进一步改进设计(一切都范化为 AttrList 和 AttrList 的套娃,Field 的 name,value 不过是一种特定的 attr,但保留有一定好处,与 consteval 有关,这影响到了 BaseList 的 Find 和 Get,没法直接做 Get(name))
【更新】
- (完成)舍弃 Union(这创造了冗余的静态信息),并提供一系列 DFS 式算法
- 支持虚基类
该库核心就在设计上,在纷杂的概念中化繁为简,精心设计各类(模板参数该是哪些,成员该是哪些,哪些概念可以合并,怎样尽量保留元信息,怎样不限制表达能力,怎样实现静态)。
归功于巧妙的设计,该库才能用 99 行就写完,而且功能依然完备强大。
属性是反射的关键特性,比如 UE 的 UPROPERTY,在 C++ 中,有一个语法叫属性列表,就适合作为属性的支持语法。
属性由名字和值组成,可能没有值,值的类型是任意的(整数,字符串,甚至是 std::pair)。
该库用 template Attr {...} 来表示属性
注意,C++ 的属性列表目前是没有用的,我示例中写上纯粹为了好看(其实是为了给程序员查看以及parser处理使用)
一个元素可能有多个属性,我们将多个属性记成属性列表,该库中为 template AttrLis,它是一个 std::tuple
后边还有需要列表的情况,因此抽出一个公共的基类——基础列表 template BaseList,内含一些查询功能
我们需要反射的东西很多
对于(非)静态成员变量,(非)静态成员函数,enum 的 key,我们统一成“域”
域也是由名字,值和属性列表组成,在不同情况下,域值含义不同
域值含义分类
该库中用 template Field 来表示域
一个对象(类,enum)有多个域,因此需要一个域列表
该库中用 template FieldList 来表示域列表
类型信息是用户需要自行填充的部分
用户需要填写
该库中用 template TypeInfo 表示
基于良好的类型设计,该库天然支持模板和 enum
一个类可能有多个基类,我们反射库也记录该信息,多个基类信息构成一个类型信息列表
该库中用 template TypeInfoList 表示
(目前不考虑虚继承)
继承关系是有向无环图,那么遍历就需要考虑 DFS 或 BFS,我提供了 DFS 算法,可在 3.4 节看到。
DFS 过程中最复杂的是 cast 的问题,特别是子类向父类的“forward cast”,大家可在该库中找到 TypeInfoBase 看看相关代码。
前边有提到,用户需要编写声明。
声明的编写是很简单的,以至于根本不需要宏。
考虑最简单的例子
对应的声明为
名字 name 的声明其实是不必要的,用开源库 nameof(底层用了 __FUNC_SIG__)就可以自动生成名字
此外,"x", &Point::x 是可以用宏解决冗余的,只要你喜欢 (大概写成FIELD(x))
测试代码位于 USRefl_99_test
涉及特性:基础,静态,成员变量,成员函数
注意,这里 struct Point 中有很多属性列表,纯粹是为了好看写上去的,有读者误以为这是侵入式,特此声明
有看我上一篇文章的读者就知道,这里的属性列表是供 parser 用的,你完全可以不写,不用 parser,单纯只在类外边写属性声明
但这很蠢,想想,属性是给谁看的,程序员,直接写在旁边才是合理的,就像注释一样,这不叫侵入式,这是必要信息,侵入式的应该是那些在类内写了 XXXRefl(Point) 冗余信息的方案。 你可以把这里的属性列表当做是有结构的注释,最终会被手动声明为元信息
---
对于声明部分,用宏来辅助写和完全手写,这两者我选择后者
原因 1:宏相当于一门新语言,不仅丑,而且需要学习(有固定的格式)
原因 2:完全手写并未有过多的冗余内容(大部分静态库却特别多冗余内容),还是 C++,能助用户更大的自由度(比如自己补充更多的元信息进去)
运行结果
涉及特性:模板(类,模板属性值)
运行结果
特性:enum(属性,静态分派)
注意,属性里塞了个函数指针
运行结果
涉及特性:继承(DFS 遍历域,菱形继承,遍历基类)
运行结果
涉及特性:函数参数列表(名字+默认值)
套娃警告(Attr 的值里塞了 AttrList)
---
未来应该会把 argument list 直接做进 Field 里,这样 test_function 部分可以简化
运行结果
我本来只是想玩一玩,因为最近没事情,比较轻松,没想到运气好找到了一种极佳的设计,耗时也就 1 天吧。
其实本来是 200+ 行的,我特地对其进行了压缩,在压缩过程中也发现了一些更好的代码风格。
我个人认为该库已经接近顶尖水平了,如果读者有什么建议欢迎提出。