从大佬那里转来的播客

pod 类型编译期反射
反射是一种根据元数据来获取类内部信息的机制,通过元数据就可以获取对象的字段和方法等信息。C# 和 Java 的反射机制都是通过获取对象的元数据来实现的。反射可以用于依赖注入、ORM 对象-实体映射、序列化和反序列化等与对象本身信息密切相关的领域。比如 Java 的 Spring 框架,其依赖注入的基础是建立在反射的基础之上的,可以根据元数据获取类型的信息并动态创建对象。ORM 对象-实体之间的映射也是通过反射实现的。Java 和 C# 都是基于中间运行时的语言,中间运行时提供了反射机制,所以反射对于运行时语言来说很容易,但是对于没有中间运行时的语言,要想实现反射是很困难的。

在2016年的 CppCon 技术大会上,Antony Polukhin 做了一个关于 C++ 反射的演讲,他提出了一个实现反射的新思路,即无需使用宏、标记和额外的工具即可实现反射。看起来似乎是一件不可能完成的任务,因为 C++ 是没有反射机制的,无法直接获取对象的元信息。但是 Antony Polukhin 发现对 pod 类型使用 Modern C++ 的模版元技巧可以实现这样的编译期反射。他开源了一个 pod 类型的编译期反射库 magic _ get(https://github.com/apolukhin/magic_get),这个库也准备进入 boost。我们来看看 magic _ get 的使用示例。

#include

本文将通过分析 magic _ get 源码来介绍 magic _ get 实现的关键技术,深入解析实现 pod 类型反射的原理。

关键技术
实现 pod 类型反射的思路是这样的:先将 pod 类型转换为对应的 tuple 类型,接下来将 pod 类型的值赋给 tuple,然后就可以通过索引去访问 tuple 中的元素了。所以实现 pod 反射的关键就是如何将 pod 类型转换为对应的 tuple 类型和 pod 值赋值给 tuple。

pod 类型转换为 tuple 类型
pod 类型对应的 tuple 类型是什么样的呢?以上面的 foo 为例,foo 对应的 tuple 应该是 tuple<int, char>,即 tuple 中的元素类型和顺序和 pod 类型中的字段完全一一对应。

根据结构体生成一个 tuple 的基本思路是,按顺序将结构体中每个字段的类型萃取出来并保存起来,后面再取出来生成对应的 tuple 类型。然而字段的类型是不同的,C++ 也没有一个能直接保存不同类型的容器,因此需要一个变通的方法,用一个间接的方法来保存萃取出来的字段类型,即将类型转换为一个 size _ t 类型的 id,将这个 id 保存到一个 array<size_t, N> 中,后面根据这个 id 来获取实际的 type 并生成对应的 tuple 类型。

这里需要解决的一个问题是如何实现类型和 id 的相互转换。

type 和 id 在编译期相互转换
先借助一个空的模版类用来保存实际的类型,再借助 C++ 14 的 constexpr 特性,在编译期返回某个类型对应的编译期 id,就可以实现 type 转换为 id 了。具体代码如下:

https://ipad-cms.csdn.net/cms/article/code/3445
上面的代码在编译期将类型 int 和 char 做了一个编码,将类型转换为一个具体的编译期常量,后面就可以根据这些编译期常量来获取对应的具体类型。

编译期根据 id 获取 type 的代码如下:

constexpr auto id_to_type( std::integral_constant<std::size_t, 6=""> ) noexcept { int res{}; return res; }constexpr auto id_to_type( std::integral_constant<std::size_t, 9=""> ) noexcept { char res{}; return res; }</std::size_t,></std::size_t,>
上面的代码中 id _ to _ type 返回的是 id 对应的类型的实例,如果要获取 id 对应的类型还需要通过 decltype 推导出来。magic _ get 通过一个宏将 pod 基本类型都做了一个编码,以实现 type 和 id 在编译期的相互转换。

#define REGISTER_TYPE(Type, Index) \ constexpr std::size_t type_to_id(identity) noexcept { return Index; } \ constexpr auto id_to_type( std::integral_constant<std::size_t, index=""> ) noexcept { Type res{}; return res; } // Register all base types here REGISTER_TYPE(unsigned short , 1) REGISTER_TYPE(unsigned int , 2) REGISTER_TYPE(unsigned long long , 3) REGISTER_TYPE(signed char , 4) REGISTER_TYPE(short , 5) REGISTER_TYPE(int , 6) REGISTER_TYPE(long long , 7) REGISTER_TYPE(unsigned char , 8) REGISTER_TYPE(char , 9) REGISTER_TYPE(wchar_t , 10) REGISTER_TYPE(long , 11) REGISTER_TYPE(unsigned long , 12) REGISTER_TYPE(void* , 13) REGISTER_TYPE(const void* , 14) REGISTER_TYPE(char16_t , 15) REGISTER_TYPE(char32_t , 16) REGISTER_TYPE(float , 17) REGISTER_TYPE(double , 18) REGISTER_TYPE(long double , 19)</std::size_t,>
将类型编码之后,保存在哪里以及如何取出来是接着要解决的问题。magic _ get 通过定义一个 array 来保存结构体字段类型 id。

template <class t,="" std::size_t="" n=""> struct array { typedef T type; T data[N]; static constexpr std::size_t size() noexcept { return N; } };
array 中的定长数组 data 中保存字段类型对应的 id,数组下标就是字段在结构体中的位置索引。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值