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

1 模板参数表指定类型判断

  • rttr/detail/misc/misc_type_traits.h

判断一个类型是不是在类型列表中

比如有这样一个场景,有 as_raw_pointer, as_object, as_std_shared_ptr 三种类型,需要判断用户模板传入的一个类型是不是这三个其中的一个。

struct as_raw_pointer {};
struct as_object {};
struct as_std_shared_ptr {};

using constructor_policy_list = type_list<as_raw_pointer, as_object, as_std_shared_ptr>;

// 希望得到的方式
template <T>
struct Check {
    // 比如这里需要判断这个T是不是 as_raw_pointer, as_object, as_std_shared_ptr 其中的一种。
}

首先需要想到的应该是下面的这个模板类,传入一个类型 Types_To_Find 和 类型列表 Types_To_Iterate,判断 Types_To_Find 是否包含在 Types_To_Iterate 中。

template <typename... Types_To_Find, typename... Types_To_Iterate>
struct find_types {
    using type = ...  // 这里的 type 应该就是包含了比较结果的一个类型。
}

那么把上面的这个模板类稍微扩展一下。既然有一个被比较的列表,又有一个需要比较的列表,那不妨把类型列表用一个类包起来,这样就有下面这个 type_list。

template <typename... Ts>
struct type_list {};
同时为了避免 type_list 套 type_list,这里可以在添加一个类型 as_type_list。
// 非特化版本,效果与 type_list<Ts...> 一样
template<typename... T>
struct as_type_list
{
    using type = type_list<T...>;
};
// 特化版本,这里即使传入的是 type_list<Ts...> ,type也可以轻松化解,最终只会有一层 type_list
template<template<class...> class Type_List, typename... Ts>
struct as_type_list<Type_List<Ts...>>
{
    using type = type_list<Ts...>;
};

然后能想象的到的就是调用下面的find_types,比如需要找 as_object 是不是在列表里,就可以通过 find_types<as_object, constructor_policy_list>::type 得到一个type,而我们希望的这个type是 type_list<as_object> ,而如果得到的是 type_list<> 则表示被测试类型不在类型列表中。

template<typename Types_To_Find, typename Types_To_Iterate>
using find_types = find_types_impl<type_list<>, as_type_list_t<Types_To_Find>, 
                                   as_type_list_t<Types_To_Iterate>>::type;

下面就需要实现 find_types_impl 来做实际判断。在特化方案1里面,Rs… 是最终结果,因此在比较之前,应该是空的,这个对应上面find_types_impl的第一个类型参数 type_list<> ,然后通过 contains 判断类型 T 是否包含在 Types_To_Find 列表中,如果在的话,则通过 type_list<Rs…, T> 给 type_list 的模板参数加一个模板类型 T,并且将这个T从Tail…中移除掉,并且递归的再进行一次。如果不包含类型T,则不添加到结果列表中,并且也从Tail…中移除T,并且也同样在进行一次,直到Tail…中所有的类型即参与比较的类型都比较完,进入特化方案2,最终得到的 type 就是用 type_list 包装过的结果。最后只要通过 sizeof… 结果中的模板参数数量,就可以知道是否包含了。

// 非特化方案
template<typename Result_List, typename Types_To_Find, typename Types_To_Iterate>
struct find_types_impl;
// 特化方案1 这里把要比较的类型拆解成 T 和 Tail... 一个一个比较
template<typename...Rs, typename Types_To_Find, typename T, typename...Tail>
struct find_types_impl<type_list<Rs...>, Types_To_Find, type_list<T, Tail...>>
{
    using type = conditional_t<contains<T, Types_To_Find>::value,
                  typename find_types_impl<type_list<Rs..., T>, 
                           Types_To_Find, type_list<Tail...>>::type,
                  typename find_types_impl<type_list<Rs...>, 
                           Types_To_Find, type_list<Tail...>>::type
                 >;
};
// 特化方案2 这里是已经比较完了,把结果通过 type_list 包装给type。
template<typename...Rs, typename...Tf>
struct find_types_impl<type_list<Rs...>, type_list<Tf...>, type_list<>>
{
    using type = type_list<Rs...>;
};

上面代码的 contains 的作用是比较一个类型 T 是不是在类型列表中。这里首先通过 std::same<T, Types>::value… 判断 T 和每一个Types比较形成一个 bool… 的参数表,然后调用 static_any_of<bool…> 来检查这个 bool… 是否有值为 true 的参数。这里 contains 继承了 static_any_of ,因为 static_any_of 是一个 std::true_type 或者 std::false_type ,contains 可以天然通过判断 value 就知道是 true 还是 false 而得到结果。

在特化方案中,主要是为了拆解 Types 是 type_list<Ts…> 的情况,拆解出参数表以后再继承非特化的 contains 就可以判断结果了。

// 非特化方案
template<typename T, typename... Types>
struct contains : static_any_of<std::is_same<T, Types>::value...>
{
};
// 特化方案
template<typename T, template<class...> class TContainer, typename... Types>
struct contains<T, TContainer<Types...>> : contains<T, Types...>
{
};

上面的 static_any_of 是用来检查 bool… 列表中有没有 true 值的。首先通过拆解 bool… 列表,拆成一个 bool 和 一包 bool…,如果拆解出来的 bool 是 true,那么会走入特化方案1,终止比较,返回一个继承 std::true_type 的类型。如果是 false 的话会走入特化方案2,并通过再次继承 static_any_of ,再次分解一个 bool 判断,如果一直循环到 static_any_of<>都没有办法得到一个 true 的话,那么继承 std::false_type ,外部通过 value 取值的时候将会得到 false。

// 非特化方案,不会被引用到
template <bool... b> struct static_any_of;
// 特化方案1
template <bool... tail>
struct static_any_of<true, tail...> : std::true_type {};
// 特化方案2
template <bool... tail>
struct static_any_of<false, tail...> : static_any_of<tail...> {};
// 特化方案3
template <> struct static_any_of<> : std::false_type {};

整个过程用逻辑表示的话其实是2层循环,第一层训话是循环是循环要比较的类型,每次循环取出一个类型,再循环与目标类型列表进行比较,如果某一次类型一样,则返回 true 并终止循环,如果到循环结束也没有匹配的话则返回false。

最后的结果是一个 type_list<Ts…> Ts… 就是匹配到的类型,如果一个也没有匹配到的话,那么得到 type_list<> ,通过 sizeof… 判断 type_list 类型参数的个数就知道结果了。代码如下:

// 非特化方案,不会调用
template<typename T>
struct type_list_size_impl;
// 特化方案1,得到模板参数个数
template<typename...Ts>
struct type_list_size_impl<type_list<Ts...>> : std::integral_constant<std::size_t, sizeof...(Ts)>
{
};
// 通过调用 type_list_size<Ts...> 可以得到模板参数的个数。
template<typename...T>
using type_list_size = typename type_list_size_impl<as_type_list_t<T...>>::type;

发表评论