原创文章,未经许可,禁止转载!

4 variant policy选择调用

  • rttr/detail/variant/variant_data_policy.h

variant_data_policy的选择

variant_policy的功能就是对包裹在variant里的数据本体进行操作,由于C++的数据类型过多,rttr将policy分成了以下9种。

  • variant_data_policy_empty //构造policy时的默认值
  • variant_data_policy_void // void类型policy
  • variant_data_policy_nullptr_t // nullptr类型的policy
  • variant_data_policy_string // std::string类型和一维char类型的policy
  • variant_data_policy_arithmetic // 算术类型的policy(int, short …)
  • variant_data_policy_small // 小容量类型的类, 容量小于variant_data
  • variant_data_policy_array_small // 小容量的数组,容量小于variant_data
  • variant_data_policy_big // 大容量的类,容量大于 variant_data
  • variant_data_policy_array_big // 大容量类型,超过variant_data容量的类型

对于给定的类型,首先要给定这个类型选用合适的policy,判定方法如下。下面的代码看似很吓人,其实用逻辑代码来表示的话无非就是套了好几层的 if-else,注释也都写在里面了,最终的目的就是从上述的9个policy里面找到一个合适 T类型 的policy。

template<typename T>
using variant_policy = 
// 判断是否是void,如果类型T是void_variant_type,
// 则variant_policy = variant_data_policy_void,否则继续向下判断
conditional_t<std::is_same<T, void_variant_type>::value,
    variant_data_policy_void,
    // 判断是不是nullptr
    conditional_t<is_nullptr_t<T>::value,
        variant_data_policy_nullptr_t,
        // 判断是不是std::string或者是一维的char数组
        conditional_t<std::is_same<T, std::string>::value || is_one_dim_char_array<T>::value,
            variant_data_policy_string,
            // 判断policy_data的容量是否能够存下T类型的数据
            conditional_t<can_place_in_variant<T>::value,
                // 判断是否为算数值
                conditional_t<std::is_arithmetic<T>::value,
                    variant_data_policy_arithmetic<T>,
                    // 判断是否是数组,这个数组的总容量是小于variant_data的容量的
                    conditional_t<std::is_array<T>::value,
                        variant_data_policy_array_small<T>,
                        // 判断是否是枚举类型
                        conditional_t<std::is_enum<T>::value,
                            variant_data_policy_small<T, default_type_converter<T, convert_from_enum<T>>>,
                            variant_data_policy_small<T>
                        >
                    >
                >,
                // 这一部分是前面判断容量大小的另外半段,
                // 这里开始的数据是无法直接放入variant_data的,
                // 因此variant_data在这里都是作为指针用
                // 判断是否为数组
                conditional_t<std::is_array<T>::value,
                    variant_data_policy_array_big<T>,
                    // 判断是否是枚举类型
                    conditional_t<std::is_enum<T>::value,
                        variant_data_policy_big<T, default_type_converter<T, convert_from_enum<T>>>,
                        variant_data_policy_big<T>
                    >
                >
            >
        >
    >
>;

判断代码里的 void_variant_type 只是一个标志性的类型,标记void类型,定义是 struct void_variant_type {}。这种空的类一般都是标记某种特定的类型用的,并不会创建出对象,这种方法在 rttr 里面大量的使用。另外,default_type_converter<T, convert_from_enum<T>> 类型是用做数值和枚举之间的转换用的。

policy的选择判定大多数用的都是 std 标准模板元,比较特殊的判定方法如下:

  • can_place_in_variant 定义如下:

这里的判断就和之前 variant_data 容量和对齐确定方法一样,判断 sizeof(T)std::alignment_of(T) 的值是否满足variant_data 的size 。

template<typename T, bool Can_Place = (sizeof(T) <= sizeof(variant_data)) 
                        && (std::alignment_of<T>::value <= 
                        std::alignment_of<variant_data>::value)>
using can_place_in_variant = std::integral_constant<bool, Can_Place>;
  • is_one_dim_char_array 定义如下:
// 用std::is_array<T>判断T类型是否为数组
// 在用raw_array_type_T<T>来判断数组的元素类型是否为char
// 再用std::rank<T>来判断数组的维度是否为1
// 这样做的目的其实就是判定T是否为一个字符串数组
template<typename T>
using is_one_dim_char_array = 
std::integral_constant<bool, std::is_array<T>::value &&
    std::is_same<char, raw_array_type_t<T>>::value &&
    std::rank<T>::value == 1
>;

raw_array_type_t 的实现方法如下,如果传入的不是数组类型应该在外部就被剔除掉了,这里不做讨论。传入的如果是一个数组类型首先会匹配4,然后会将数组的 const 和 volatile 属性去掉后匹配 2或者3 模式,在这两个模式中,元素的类型会被拆出来拿到然后匹配1,在1中去掉数组元素类型的 const, volatile, reference, pointer ,就可以拿到退化后的元素类型,从而判断这个类型是不是char 类型。

template<typename T, typename Enable = void>
struct raw_array_type  // 1
{
    // raw_type_t 的作用是去除类型T的const, volatile, referernce, pointer属性
    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]>  // 2
{
    using type = typename raw_array_type<T>::type;
};

template <typename T>
struct raw_array_type_impl<T[]>  // 3
{
    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> // 4
{
    using type = typename raw_array_type_impl<remove_cv_t<T>>::type;
};

template<typename T>
using raw_array_type_t = typename raw_array_type<T>::type;

variant_data_policy可以做那些操作

variant_data_policy除了create以外都是用枚举来表示操作类型的,通过调用variant_data_policy的invoke函数给予如下的操作类型操作variant。create操作独立于invoke的操作流程,单独调用create函数操作。

  • DESTROY // 销毁
  • CLONE // 克隆给argment
  • SWAP // 将variant的数据和传入的arg进行对调
  • EXTRACT_WRAPPED_VALUE // 如果variant_data的类型是被包装过的,把包装在里面的原始值返回出来,比如被std::shared_ptr<T>包装,就返回T*。
  • CREATE_WRAPPED_VALUE // 创建
  • GET_VALUE // 将variant_data的值解出来
  • GET_TYPE // 获取variant_data原本类型的type实例
  • GET_PTR // 获取指针
  • GET_RAW_TYPE // 获取variant_data原本退化类型的type实例
  • GET_RAW_PTR // 获取variant_data原本退化类型的type指针
  • GET_ADDRESS_CONTAINER // 通过data_address_container,返回数据的地址和类型
  • IS_ASSOCIATIVE_CONTAINER // 返回是否std::map类型
  • IS_SEQUENTIAL_CONTAINER // 返回是否std::vector类型
  • CREATE_ASSOCIATIV_VIEW // 创建std::map的view,通过view可以存取map的元素
  • CREATE_SEQUENTIAL_VIEW // 创建std::vector的view,通过view可以存取vector的元素
  • IS_VALID // 判断是否有效
  • IS_NULLPTR // 判断是否是nullptr_t类型
  • CONVERT // 将variant_data数据转换为给定的类型
  • COMPARE_EQUAL // 比较是否等于
  • COMPARE_LESS // 比较是否小于

发表评论