- rttr/variant.h
- rttr/variant.cpp
- rttr/detail/variant/variant_impl.h
rttr的variant主要的功能将对象类型擦除后用于统一传递和保存,同时在variant构造的时候会同时保存传入对象的type信息,方便以后恢复。
variant的构造
先看variant一个最重要的构造函数。
template<typename T, typename Tp = detail::decay_variant_t<T>>
variant(T&& val) : m_policy(&detail::variant_policy<Tp>::invoke)
{
detail::variant_policy<Tp>::create(std::forward(val), m_data);
}
注意:列出的代码做出一部分简化,请以源作者代码为准
构造函数有一个模板实参,支持任意类型,本质上就是要把任意类型的对象包裹到variant里去。模板的第二个类型 Tp = detail::decay_variant_t<T>> 是将T类型做一个退化,这里的退化过程不是使用的std::decay,不同于std::decay的地方在于这里的退化不对数组进行退化,简而言之就是不会把 T[] 退化成 T*。
- m_policy的初值
m_policy的值是根据类型T推导出对应的variant_policy的invoke函数指针,因为variant是将变量的类型擦除后保存的,因此变量的一些必要的操作就要通过variant_policy去执行,而m_policy赋值就是将对应policy的静态成员函数invoke的函数指针赋给它,之后就可以通过调用m_policy,并传递操作类型就可以操作variant里包裹的数据了。下面是部分关于数值类型variant_policy的处理:
template <typename T>
struct RTTR_API variant_data_policy_arithmetic
{
static bool invoke(variant_policy_operation op,
const variant_data& src_data, argument_wrapper arg)
{
switch (op)
{
case variant_policy_operation::GET_VALUE:
{
arg.get_value<const void*>() = &reinterpret_cast<const T&>(src_data);
break;
}
... // 为了方便理解,代码有修改
这里的invoke只展示了GET_VALUE的操作,arg.get_value在上一章就讲过了,等号前面是将arg.m_data强制转成const void*,用来存放等候后面src_data强制转换成T的地址,这样variant的值就可以通过arg传递到函数外面了,但是生存期仍然由variant控制。
- m_data的初值
m_data 类型是variant_data,定义如下:
using variant_basic_types = type_list<bool,
signed char, unsigned char, char, wchar_t,
short int, unsigned short int, int, unsigned int,
long int, unsigned long int, long long int,
unsigned long long int, float, double, void*>;
using variant_data = std::aligned_storage<max_sizeof_list<variant_basic_types>::value,
max_alignof_list<variant_basic_types>::value>::type;
variant_basic_types是一个类型的列表,通过max_sizeof_list和max_alignof_list找到这个列表中大小(sizeof)和对齐大小(alignof)最大的两个值,通过std::aligned_storage定义这样的一个空间。因此,m_data所分配到的空间是可以放下以上所有类型中的任意一个,类似于union,并且是对齐的。
下面来看一下max_sizeof_list的实现,
template <typename T, typename...Ts>
struct max_sizeof_list_impl;
template <typename T>
struct max_sizeof_list_impl<T>
{
static size_t value = sizeof(T);
};
template<typename T1, typename T2, typename... U>
struct max_sizeof_list_impl<T1, T2, U...>
{
static std::size_t value =
max_sizeof_list_impl<std::condition_t<sizeof(T1) >=
sizeof(T2), T1, T2>, U...>::value;
};
template<typename... Ts>
using max_sizeof_list = std::integral_constant<std::size_t,
max_sizeof_list_impl<Ts...>::value>;
max_sizeof_list是一个constexpr的值,通过递归的推导 max_sizeof_list_impl<T1, T2, U…>,最终得到唯一的一个类型 ,最后通过 max_sizeof_list_impl<T> 得出最终的sizeof(T)的值,也就是这些类型中最大的一个。
max_alignof_list 的值的获取方法也是一样,只不过比较过程中用的不是 sizeof,而是 std::alignment_of,同样的方法就可以得到最大的对齐值。