- rttr/type.h
- rttr/wrapper_mapper.h
- rttr/detail/type/type_impl.h
- rttr/detail/type/type_data.h
- rttr/detail/type/type_data.cpp
- rttr/detail/impl/wrapper_mapper_impl.h
type实例的保存在第一章已经讲过,是通过模板类的单例来实现的,因此创建type实例的过程就是通过 type::get<T> 的方式去创建,既可以是主动的,也可以是被动的。
- 被动就是在使用的时候去get一个type,如果从程序启动开始一直没有去get这个类型的type,那么rttr就会创建一个。
- 主动就是在程序初始化的某一刻,将需要的type全部都get一遍,这样,在程序真正运行过程中就不需要去创建这些类型了。这种方式在后面注册class的时候会用到。
type实例这种单例的方式主要是要跟类型绑定,保证一个类型一个type实例,而真正存储type信息的是type类中的一个成员变量:type_data,它的定义如下:
struct RTTR_LOCAL type_data
{
// 如果type_data是被包装过的,那么raw_type_data拿到的是原本的类型信息,
// 并且是退化过的,如果没有被包装过拿到的是get_invalid_type_data()
type_data* raw_type_data;
// 如果这个类型是被包装过的,那么wrapped_data是被包裹着的真正的类型type_data
type_data* wrapped_type;
// 如果是数组,array_raw_type是数组元素的type_data
type_data* array_raw_type;
// 类型的名称
std::string name;
// string_view格式的类型名称
string_view type_name;
// sizeof(T)
std::size_t get_sizeof;
// 传入的T类型有几层指针
std::size_t get_pointer_dimension;
// 调用create_variant函数生成variant的函数指针
impl::create_variant_func create_variant;
// 基类列表
impl::get_base_types_func get_base_types;
// 枚举包装
enumeration_wrapper_base* enum_wrapper;
// 获取对象注册时的meta信息
impl::get_metadata_func get_metadata;
// 按T的包装方式创建包装类型的实例
impl::create_wrapper_func create_wrapper;
// 拿到注册对象的属性,函数,构造等
impl::get_class_data_func get_class_data;
// 该类型是否有效
bool is_valid;
// 类型的标记
type_traits m_type_traits;
};
m_type_data构造
type_data的构造在函数make_type_data里进行,通过new一个type_data,然后对type_构造赋值,并将type_data的指针包装成unique_ptr返回给type,这样唯一的type就有了唯一的type_data,部分代码如下,
template<typename T>
std::unique_ptr<type_data> make_type_data()
{
std::unique_ptr<type_data> obj = std::unique_ptr<type_data>
(
new type_data
{
// 找到相应的type_data赋值给raw_type_data
raw_type_info<T>::get_type().m_type_data,
// 找到相应的type_data赋值给wrapper_type
wrapper_type_info<T>::get_type().m_type_data,
// 找到相应的type_data赋值给array_raw_data
array_raw_type<T>::get_type().m_type_data,
...
}
)
return obj;
}
1. raw_type_data的初始值
raw_type_data的初值是通过raw_type_info<T>获取对应类type,从type中获取对应的type_data。raw_type_info<T>推导type的过程如下,
// 非特化
// 判断T与退化类型的T是否相同,相同则返回invalid_type
template<typename T, bool v = std::is_same<T, typename raw_type<T>::type >::value>
struct raw_type_info
{
static type get_type()
{
return get_invalid_type();
} // we have to return an empty type, so we can stop the recursion
};
// 特化
template<typename T>
struct raw_type_info<T, false>
{
static type get_type()
{
return type::get<typename raw_type<T>::type>();
}
};
选择的依据是判断类型T与退化类型T是否一样,一样的话匹配非特化方案,返回invalid_type,因为本身T就是原始类型了,因此也没有必要在保存一个原始类型。如果不一样,则匹配特化方案,raw_type_data会返回T的退化类型的type。
这里的退化规则是:
- 如果是引用,则首先去除引用。
- 如果是指针但不是函数指针(保留函数指针),则移除指针。
- 最后移除const和volatile。
invalid_type的type_data是 invalid_type_data的单例,初始化时,invalid_type_data的3个type_data在创建是都被赋予nullptr,因此他本身和其他的type之间也没有什么关系,也不需要去保留其他的type_data的指针。同样,这个对象也是一个静态对象,因此也是全局唯一。
static type_data& get_invalid_type_data_impl() RTTR_NOEXCEPT
{
static type_data instance{
nullptr,
nullptr,
nullptr,
...
return instance;
}
2. wrapped_type的初始值
wrapped_type的初值是通过wrapper_type_info<T>获取对应类type, 从type中获取对应的type_data ,推导过程如下:
// 如果是包装类型,则返回被包装的原始类型的type
template<typename T, bool = is_wrapper<T>::value>
struct wrapper_type_info
{
static type get_type()
{
return type::get<wrapper_mapper_t<T>>();
}
};
// 如果不是包装类型,返回invalid_type
template<typename T>
struct wrapper_type_info<T, false>
{
static type get_type()
{
return get_invalid_type();
}
};
通过is_wrapper<T>判断类型T是否是被包装过的,如果是的话返回包装类型里的原始type,如果不是包装类型则返回invalid_type(),外部的wrapped_data通过获取返回type的type_data就可以知道当前类型是不是包装类型,被包装的原始类型是什么。
关于判断是否是包装类型is_wrapper<T>的推导方法如下:
struct invalid_wrapper_type { };
template<typename T>
using is_wrapper = std::integral_constant<bool,
!std::is_same<invalid_wrapper_type, wrapper_mapper_t<T>>::value >;
比较wrapper_mapper_t<T>推导出来的类型是不是invalid_wrapper_type,invalid_wrapper_type是一个标记的类型,没有实质用处。推导方法如下:
template<typename T>
using wrapper_mapper_t = typename wrapper_mapper<typename remove_cv<
typename std::remove_reference<T>::type
>::type>::wrapped_type;
先要将T类型的const,voilate和reference去掉,然后通过wrapper_mapper往下推,
// 非特化情况,因此认为没有包装类型,原本的T类型不需要处理
template<typename T>
struct wrapper_mapper
{
using wrapped_type = detail::invalid_wrapper_type;
using type = T;
};
// 特化情况,识别出std::shared_ptr<T>类型
template<typename T>
struct wrapper_mapper<std::shared_ptr<T>>
{
using wrapped_type = decltype(std::shared_ptr<T>().get()); // T*
using type = std::shared_ptr<T>;
...
};
当没有匹配到特化情况的时候,可以认为T类型是没有被包装,因此当前的类型就是T,而包装类为了让外部能识别和比较,就用了标记性的类型 invalid_wrapper_type。
如果匹配到了特化情况,这里以std::shared_ptr<T>做范例,明显可以确认外部测试的类型就是std::shared_ptr<T>,而分离出的T是被包装的原始类型,事实上,智能指针里面包裹的是一个对象的裸指针,因此,wrapped_type的类型其实应该是T*。
用同样的方法就可以判断出std::unique_ptr,std::weak_ptr,如果你觉得这些包装还不能满足你的需求,也可以自己增加一些偏特化类型来判断处理。
array_raw_type的初始值
array_raw_type的初值是通过 array_raw_type <T>获取对应类type, 从type中获取对应的type_data , 推导过程如下:
// 特化方案,通过raw_array_type_t<T>获取数组元素类型,然后通过类型获取type返回
template<typename T, bool = std::is_array<T>::value>
struct array_raw_type
{
static RTTR_INLINE type get_type()
{
return type::get<raw_array_type_t<T>>();
}
};
// 非特化方案,直接返回invalid_type
template<typename T>
struct array_raw_type<T, false>
{
static RTTR_INLINE type get_type()
{
return get_invalid_type();
}
};
通过std::is_array<T>可以很方便的判断出T类型是否是数组类型,如果是的话继续推导出数组元素的类型,如果不是的话返回invalid_type的,这样外部就可以拿invalid_type_data赋值给array_raw_type。
关于raw_array_type<T>::type推导数组元素的方法如下,
// 非特化方案,取到了元素的类型,在进一步去除约束即可
template<typename T, typename Enable = void>
struct raw_array_type
{
using type = raw_type_t<T>;
};
template<typename T>
struct raw_array_type_impl;
template<typename T, std::size_t N>
struct raw_array_type_impl<T[N]>
{
using type = typename raw_array_type<T>::type;
};
template <typename T>
struct raw_array_type_impl<T[]>
{
using type = typename raw_array_type<T>::type;
};
//特化方案
template<typename T>
struct raw_array_type<T, typename std::enable_if<std::is_array<T>::value>::type>
{
using type = typename raw_array_type_impl<remove_cv_t<T>>::type;
};
这里有两次推导过程,raw_array_type<T>的非特化部分是拿到数组元素的类型的,去除相应的约束即可拿到真正的类型返回。如果匹配到了特化方案,也就是T满足数组类型,那么会继续通过raw_array_type_impl推导,查看是否满足T[]或者T[N],如果是的话则分离出元素类型,然后在回到raw_array_type的推导,如果数组已经拆完了,那么会推导到raw_array_type的非特化版本里去,如果拆完的类型任然是数组,则循环往下推导,直至推出最终的元素type,array_raw_type就可以拿到这个类型的type data了。
至此,三个type data的赋值过程都已经完成了。
后面紧接是name和type_name的赋值,也就是这个type的字符串名称,这两个变量保存的字符串是一样的,只不过name的类型是std::string,而typename的类型是string_view,string_view是rttr实现的string类型,本质和std::string是一样的,只不过效率更高,存储空间更少。这两个字符串的值是通过调用get_type_name<T>,get_type_name<T>在通过 __FUNCSIG__(windows),__PRETTY_FUNCTION__(mac,Linux)获取模板函数签名字符串,通过字符串截断,仅保留类型部分作为type_data的字符串描述。这个字符串的描述也可以通过detail::type_register::custom_name来修改,这部分会在注册class的时候在讲到。
new type_data
{
...
// name =
::rttr::detail::get_type_name<T>().to_string(),
// typename =
::rttr::detail::get_type_name<T>(),
get_sizeof的赋值就直接使用sizeof(T)来获取,不过void类型和function类型,get_sizeof会直接赋值。
// get_sizeof =
get_size_of<T>::value(),
get_pointer_dimension是用来维数的,也就是指针的层数,深度,计算的方法就是递归的拆解指针,拆解的类中有一个constexpr的计数,每拆一层就拿下一层的计数加1,直到解到最后一层。也就是最后一层的计数是0,他已经不是指针了,上一层指针在0上加1,就得到1层指针,以此类推,实现代码如下:
...
// get_pointer_dimension = get_pointer_dimension的赋值
pointer_count<T>::value,
...
// 指针计数
// 非特化,最后一层,已经不是指针了
template<typename T, typename Enable = void>
struct pointer_count_impl
{
static RTTR_CONSTEXPR_OR_CONST std::size_t size = 0;
};
// 特化部分,如果是指针就将下一层指针的计数加1
template<typename T>
struct pointer_count_impl<T, enable_if_t<std::is_pointer<T>::value &&
!is_function_ptr<T>::value &&
!std::is_member_pointer<T>::value>>
{
static std::size_t size = pointer_count_impl<remove_pointer_t<T> >::size + 1;
};
// 获取指针计数
template<typename T>
using pointer_count = std::integral_constant<std::size_t, pointer_count_impl<T>::size>;
create_variant的赋值,调用create_variant_func<T>其实就是选择一个variant policy,拿到policy后把create_variant的函数指针赋值给m_type_data的create_variant变量即可。
...
// create_variant =
&create_variant_func<T>::create_variant,
...
get_base_types的赋值,是拿到type的父类的列表赋值给get_base_types,关于父类列表的东西会在class部分详细讲述。
enum_wrapper,枚举的包装,直接赋值为nullptr,这块的赋值不再这里,关于枚举的东西会在enum部分讲。
get_metadata,用于类成员变量或者成员函数说明的一个列表,在注册类的时候传入,这里是将这个获取描述列表的方法赋值给get_metadata
...
// get_base_types =
&base_classes<T>::get_types,
// enum_wrapper =
nullptr,
// get_metadata =
&get_metadata_func_impl<T>,
...
假如类型T是一个包装类型,调用get_create_wrapper_func<T>()会得到create_wrapper<Wrapper, Tp>的函数指针,这里的Wrapper是传入的包装类型,比如std::shared_ptr<T>,Tp是经过wrapper_mapper推导,就是前面wrapped_type赋初值的推导过程的type,这样Tp拿到就是包装类里的原始类型。有了这两个类型,create_wrapper在调用时可以通过Tp拿到arg的真正类型,并且通过原始类型的对象通过wrapper_mapper提供的create函数创建一个包装过的对象。比如将T类型的对象重新创建一个std::shared_ptr<T>的对象并保存在variant里。
template<typename Wrapper, typename Wrapped_Type>
void create_wrapper(const argument& arg, variant& var)
{
if (arg.get_type() != type::get<Wrapped_Type>())
return;
auto& wrapped_type = arg.get_value<Wrapped_Type>();
var = wrapper_mapper<Wrapper>::create(wrapped_type);
}
template<typename Wrapper, typename Tp = wrapper_mapper_t<Wrapper>>
enable_if_t<is_wrapper<Wrapper>::value &&
::rttr::detail::is_copy_constructible<Wrapper>::value &&
std::is_default_constructible<Wrapper>::value &&
has_create_wrapper_func<Wrapper>::value, impl::create_wrapper_func>
get_create_wrapper_func()
{
return &create_wrapper<Wrapper, Tp>;
}
...
// create_wrapper =
get_create_wrapper_func<T>(),
...
get_create_wrapper_func<T>是返回T类型注册的所有的属性,函数,构造,基类列表,派生类信息等类信息列表的函数。
is_valid的赋值只要是普通的type都认为是有效的,true,只有invalid_type_data,也就是前面说的那个invalid_type_data的单例,它的is_valid是false。
m_type_traits是一个按位赋值的std::bitset<size_t>的类型,它的作用实在构造的时候给type的属性打标记。大部分都是用的std的模板元来给出标记,另外一小部分是为了满足需求自定义的判断。
- is_associative_container 判断是否关联容器,比如std::map,std::unorderd_map。
- is_sequential_container 判断是否顺序容器,比如std::vector, std::array。
- is_template_instantiation 判断类型是否使用模板。
...
// get_class_data =
get_create_wrapper_func<T>(),
// is_valid =
true,
// m_type_traits =
type_trait_value{
TYPE_TRAIT_TO_BITSET_VALUE(is_class) |
TYPE_TRAIT_TO_BITSET_VALUE(is_enum) |
TYPE_TRAIT_TO_BITSET_VALUE(is_array) |
TYPE_TRAIT_TO_BITSET_VALUE(is_pointer) |
TYPE_TRAIT_TO_BITSET_VALUE(is_arithmetic) |
TYPE_TRAIT_TO_BITSET_VALUE_2(is_function_ptr, is_function_pointer) |
TYPE_TRAIT_TO_BITSET_VALUE(is_member_object_pointer) |
TYPE_TRAIT_TO_BITSET_VALUE(is_member_function_pointer) |
TYPE_TRAIT_TO_BITSET_VALUE_2(::rttr::detail::is_associative_container,
is_associative_container) |
TYPE_TRAIT_TO_BITSET_VALUE_2(::rttr::detail::is_sequential_container,
is_sequential_container) |
TYPE_TRAIT_TO_BITSET_VALUE_2(::rttr::detail::template_type_trait,
is_template_instantiation)
...
}