@value 静态变量_"全球最强" | 99 行 C++ 静态反射库

“全球最强”之一的 C++ 静态反射库,仅 99 行

Ubpa/USRefl​github.com
239683cfff08a66a9e2c813821a8e36b.png

你想要的功能全都有

特性预览

  • 仅 99 行
  • 非侵入式
  • 基础
    • (non-static / static) 成员变量
    • (non-static / static) 成员函数
      • 参数列表(各参数名字[+默认值])
  • 属性
  • enum
    • key 和字符串互转
    • 根据 enum 值静态分派
  • 模板
    • 模板属性值
  • 继承
    • 递归(DFS)遍历基类(域)
    • 菱形继承
    • 虚继承
  • Parser
感谢
@灵剑 :讨论关于继承时域的问题,现用 DFS 实现

0. 引言

C++ 没有反射,猴年马月就有了

在猴年马月到来之前,我们需要自己实现反射

这里讨论静态反射库

开源社区上有一些反射库,如refl-cpp,我觉得它们写得很差

因此我写了一个,比它们都强,而且只要 99 行

你可能觉得,99 行能有啥功能?实际上它拥有了静态反射的所有核心部分

静态就只能做这么多了,感觉表达能力上已经是静态的极限了,再多,那就是动态反射了

1. 99 行静态反射库

源码链接:USRefl_99.h

除了 stl 没有任何其他依赖,没有任何宏,拿了直接用

99 行是压缩过的(省略了许多换行),原版本为 USRefl
【警告】
C++ 功底比较一般的读者,库的源码和设计部分可以跳过,直接看第 3 节的示例部分,看看库用起来是什么“姿势“
【其他】
如果上了 C++20,“字符串模板参数”可以进一步改进设计
【更新】
-(完成)舍弃 Union(这创造了冗余的静态信息),并提供一系列 DFS 式算法
-(完成)支持虚基类
-(新增)Accumulate + mask -> ForEach + mask
-(新增)parser
-(更新)更强大的parser
-(改版)将名字放入模板参数中(目前未在文章中更新此部分内容)

43bbf75418e09e060ecace12fffdfe31.png

2. 设计

该库核心就在设计上,在纷杂的概念中化繁为简,精心设计各类(模板参数该是哪些,成员该是哪些,哪些概念可以合并,怎样尽量保留元信息,怎样不限制表达能力,怎样实现静态)。

归功于巧妙的设计,该库才能用 99 行就写完,而且功能依然完备强大。

2.1 属性

属性是反射的关键特性,比如 UE 的 UPROPERTY,在 C++ 中,有一个语法叫属性列表,就适合作为属性的支持语法。

属性由名字和值组成,可能没有值,值的类型是任意的(整数,字符串,甚至是 std::pair)。

该库用 template<typename T> Attr {...} 来表示属性

注意,C++ 的属性列表目前是没有用的,我示例中写上纯粹为了好看(其实是为了给程序员查看以及parser处理使用)

2.2 属性列表

一个元素可能有多个属性,我们将多个属性记成属性列表,该库中为 template<typename... Attrs> AttrLis,它是一个 std::tuple

后边还有需要列表的情况,因此抽出一个公共的基类——基础列表 template<typename... Elems> BaseList,内含一些查询功能

2.2 域

我们需要反射的东西很多

对于(非)静态成员变量,(非)静态成员函数,enum 的 key,我们统一成“域”

域也是由名字,值和属性列表组成,在不同情况下,域值含义不同

域值含义分类

  • 非静态成员变量:非静态成员变量指针,类型为 T Object::*
  • 静态成员变量:静态成员变量指针,类型为 T *(注意没有 Object::*
  • 非静态成员函数:非静态成员函数指针,类型为 Func Object::*,其中 FuncRet(Args...)[const]
  • 静态成员函数:静态成员函数指针,类型为 Func*,其中 FuncRet(Args...)[const]
  • enum:enum 的 key 值,类型为 enum

该库中用 template<typename T, typename AttrList> Field 来表示域

2.3 域列表

一个对象(类,enum)有多个域,因此需要一个域列表

该库中用 template<typename... Fields> FieldList 来表示域列表

2.4 类型信息

类型信息是用户需要自行填充的部分

用户需要填写

  • 所有域及其属性列表
  • 自身的属性列表
  • 类名

该库中用 template<typename T> TypeInfo 表示

2.5 模板和 enum

基于良好的类型设计,该库天然支持模板和 enum

2.6 继承

一个类可能有多个基类,我们反射库也记录该信息,多个基类信息构成一个类型信息列表

该库中用 template<typename... TypeInfos> TypeInfoList 表示

(目前不考虑虚继承)

继承关系是有向无环图,那么遍历就需要考虑 DFS 或 BFS,我提供了 DFS 算法,可在 3.4 节看到。

DFS 过程中最复杂的是 cast 的问题,特别是子类向父类的“forward cast”,大家可在该库中找到 TypeInfoBase 看看相关代码。

另外还支持虚继承

2.7 声明

前边有提到,用户需要编写声明。

声明的编写是很简单的,以至于根本不需要宏。

考虑最简单的例子

7eebcea7f366294d9750630d0c44bc0c.png

对应的声明为

e3b5dbf3b75c2875832e8e63ecc7ce2f.png
名字 name 的声明其实是不必要的,用开源库 nameof(底层用了 __FUNC_SIG__)就可以自动生成名字
此外, "x", &Point::x 是可以用宏解决冗余的,只要你喜欢 (大概写成 FIELD(x)

另外我们提供了一个 parser,用于自动生成声明代码,不用手写

3. 示例

测试代码位于 USRefl_99_test

在线测试:Compiler Explorer - C++ (x86-64 clang 10.0.0)

3.1 极简

涉及特性:基础,静态,成员变量,成员函数

注意,这里 struct Point 中有很多属性列表,纯粹是为了好看写上去的,有读者误以为这是侵入式,特此声明
有看我上一篇文章的读者就知道,这里的属性列表是供 parser 用的,你完全可以不写,不用 parser,单纯只在类外边写属性声明
但这很蠢,想想,属性是给谁看的,程序员,直接写在旁边才是合理的,就像注释一样,这不叫侵入式,这是必要信息,侵入式的应该是那些在类内写了 XXXRefl(Point) 冗余信息的方案。 你可以把这里的属性列表当做是有结构的 注释,最终会被手动声明为元信息
---
对于声明部分,用宏来辅助写和完全手写,这两者我选择后者
原因 1:宏相当于一门新语言,不仅丑,而且需要学习(有固定的格式)
原因 2:完全手写并未有过多的冗余内容(大部分静态库却特别多冗余内容),还是 C++,能助用户更大的自由度(比如自己补充更多的元信息进去)

72beb249a3c16bce28713cd238caf2fc.png

运行结果

4a4228a73f36d8857f12e5fdedaaaad5.png

3.2 模板

涉及特性:模板(类,模板属性值)

cbcd752830d50471eea6d4fd78350dc2.png

运行结果

e07dd387a4e20ca61e16100e575a1d29.png

3.3 enum

特性:enum(属性,静态分派)

注意,属性里塞了个函数指针

90dc0fef5d535eff2dcdd3e6369ffc0e.png

运行结果

99297fe1f2725151b6931579b6bdf27e.png

3.4 继承

涉及特性:继承(DFS 遍历域,菱形继承,遍历基类)

f7f30f937f592c4cdc13b401d6dce347.png

运行结果

eaa8e512b955c3f9e8c8adce1cf1e089.png

3.5 函数

涉及特性:函数参数列表(名字+默认值)

套娃警告( Attr 的值里塞了 AttrList
---
未来应该会把 argument list 直接做进 Field 里,这样 test_function 部分可以简化

84d53eddb7b58e95acf4d8d5421d3115.png

运行结果

a5f3de23b03e5fa6bf91cb6732bc35c5.png

3.6 虚继承

涉及特性:虚继承

29671064e9c36c4bf55ae9e2be22397d.png

a9cdb8be1966f693134ee3ab33ff0a08.png

3.7 Mask

de9aaa86723649b9bc42205fd2ccd7d5.png

564124ade946739e94ff25ae8a744727.png

3.8 parser

声明的编写可能有点麻烦,声明的信息不多,主要是声明内容多,写起来费时费力。

因此我提供了 parser 以便简化该部分。

详情可阅读

Ubp.a:“轻量级” C++ Parser 方案​zhuanlan.zhihu.com
92f06ce54a8e2c3ef8a6e6e6e12c14f2.png

这里提供一下示例

9b5b2bbf6cc967239554390fb91d7e4a.png

parser 生成结果

793a5ffb340a630eb18fdab0aee6f498.png

4. 总结

我本来只是想玩一玩,因为最近没事情,比较轻松,没想到运气好找到了一种极佳的设计,耗时也就 1 天吧。

其实本来是 200+ 行的,我特地对其进行了压缩,在压缩过程中也发现了一些更好的代码风格。

我个人认为该库已经接近顶尖水平了,如果读者有什么建议欢迎提出。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值