Chinaunix首页 | 论坛 | 博客
  • 博客访问: 138980
  • 博文数量: 53
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 224
  • 用 户 组: 普通用户
  • 注册时间: 2015-06-28 01:08
文章分类
文章存档

2020年(1)

2019年(1)

2017年(1)

2016年(18)

2015年(32)

我的朋友

分类: C/C++

2020-07-13 16:07:50

你想要的功能全都有

特性预览

  • 仅 99 行
  • 非侵入式
  • 基础
    • (non-static / static) 成员变量
    • (non-static / static) 成员函数
      • 参数列表(各参数名字[+默认值])
  • 属性
  • enum
    • key 和字符串互转
    • 根据 enum 值静态分派
  • 模板
    • 模板属性值
  • 继承
    • 递归(DFS)遍历基类(域)
    • 菱形继承
编写过程中有进行参考(  trefrefl-cpp

0. 引言

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

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

这里讨论静态反射库

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

因此我写了一个,比它们都强,而且只要 99 行(refl-cpp 是 4500 行左右)

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

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

1. 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 式算法
- 支持虚基类

2. 设计

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

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

2.1 属性

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

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

该库用 template Attr {...} 来表示属性

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

2.2 属性列表

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

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

2.2 域

我们需要反射的东西很多

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

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

域值含义分类

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

该库中用 template Field 来表示域

2.3 域列表

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

该库中用 template FieldList 来表示域列表

2.4 类型信息

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

用户需要填写

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

该库中用 template TypeInfo 表示

2.5 模板和 enum

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

2.6 继承

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

该库中用 template TypeInfoList 表示

(目前不考虑虚继承)

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

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

2.7 声明

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

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

考虑最简单的例子

对应的声明为

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

3. 示例

测试代码位于 USRefl_99_test

3.1 极简

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

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

运行结果

3.2 模板

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

运行结果

3.3 enum

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

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

运行结果

3.4 继承

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

运行结果

3.5 函数

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

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

运行结果

4. 总结

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

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

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

阅读(1884) | 评论(0) | 转发(0) |
0

上一篇:Leveldb sstable 的level管理和compaction操作

下一篇:没有了

给主人留下些什么吧!~~