我如何随意排序元组的类型?

时间:2015-08-23 17:09:28

标签: c++ templates c++11 template-meta-programming

让我对C ++感到恼火的一件事是空struct / class占用空间。

所以,我有这样的想法std::tuple(或一些变体,因为它(和编译器的)实现是高度依赖于实现的)可能能够节省一天,这有点,但有由于包装和对齐引起的问题。由于编译器将如何对齐struct中的项目,因此非空白旁边的空白旁边的空白将大于2个空白旁边的空白。

因此,我需要一种基于某些标准对类型进行重新排序的方法。不需要根据大小对整个列表进行排序(并且在某些情况下可能是有害的)所以我需要一些通用的方法来重新排序元组的类型列表,但仍然像访问类型列表一样按原始顺序访问它。

我环顾四周,没有找到这样的东西,我不知所措。关于如何实现这一目标的想法?

实施例

struct A{};
struct B{};
 // Need to be reordered based on some criteria.
std::tuple<A, int, B, float> x;
// In this case move all of the empty objects together like:
//   std::tuple<A, B, int, float> x;
// but still have get<1>(x) return the `int` and get<2>(x) return `B`.
static_assert(std::is_same<decltype(get<0>()), A>::value,     "0 should be type A");
static_assert(std::is_same<decltype(get<1>()), int>::value,   "1 should be type int");
static_assert(std::is_same<decltype(get<2>()), B>::value,     "2 should be type float");
static_assert(std::is_same<decltype(get<3>()), float>::value, "3 should be type B");

这不能用手完成的原因是这可能是模板的一部分,并且元组中的元素可能是空的或不是,基于参数:

template <typename A, typename B, typename C, typename D>
class X
{
    // Need to have this auto arranged given some criteria
    // like size or move all of the empties together.
    tuple<A, B, C, D> x;
  public:
    template<int i>
    auto get() -> typename std::tuple_element<i, decltype(x)>
    {
      return get<i>(x);
    }
};
// What are these types?  Who knows.  This could be buried in some
// template library somewhere.
X<T1, T2, T3, T4> x;

2 个答案:

答案 0 :(得分:4)

首先,让我们从基础开始。我们需要一种方法将模板模板(std::tuple)转换为元函数类:

template <template <typename...> class Cls>
struct quote {
    template <typename... Args>
    using apply = Cls<Args...>;
};

一个类型列表:

template <typename... Args>
struct typelist { };

他们之间要说些什么:

template <typename F, typename TL>
struct apply;

template <typename F, typename... Args>
struct apply<F, typelist<Args...>> {
    using type = typename F::template apply<Args...>;
};

template <typename F, typename TL>
using apply_t = typename apply<F, TL>::type;

因此,给定一些类型列表,我们可以:

using my_tuple = apply_t<quote<std::tuple>, some_typelist>;

现在,我们只需要在某些条件下使用分区程序:

template <typename TL, typename F>
struct partition {
    using type = concat_t<filter_t<TL, F>,
                          filter_t<TL, not_t<F>>
                          >;
};

concat

template <typename... Args>
struct concat;

template <typename... Args>
using concat_t = typename concat<Args...>::type;

template <typename... A1, typename... A2, typename... Args>
struct concat<typelist<A1...>, typelist<A2...>, Args...> {
    using type = concat_t<typelist<A1..., A2...>, Args...>;
};

template <typename TL>
struct concat<TL> {
    using type = TL;
};

filter

template <typename TL, typename F>
struct filter;

template <typename TL, typename F>
using filter_t = typename filter<TL, F>::type;

template <typename F>
struct filter<typelist<>, F> {
    using type = typelist<>;
};

template <typename A, typename... Args, typename F>
struct filter<typelist<A, Args...>, F> {
    using type = concat_t<
                     std::conditional_t<F::template apply<A>::value,
                                        typelist<A>,
                                        typelist<>>,
                     filter_t<typelist<Args...>, F>
                     >;
};

not_

template <typename F>
struct not_ {
    template <typename Arg>
    using apply = std::conditional_t<F::template apply<Args>::value,
                                     std::false_type,
                                     std::true_type>;
};

对于您希望放入some_typelist的{​​{1}}种类型,其中包含:

tuple

答案 1 :(得分:4)

以巴里所做的为基础。

  

所以从这里开始我需要一个映射元函数来使用原始函数   指数,我该怎么做?

首先,一些助手可以方便索引映射。而且因为我懒惰,我稍微修改了typelist

template <typename... Args>
struct typelist { 
    static constexpr std::size_t size = sizeof...(Args);
};

template<class T, std::size_t OldIndex, std::size_t NewIndex>
struct index_map_leaf {
    using type = T;
    static constexpr std::size_t old_index = OldIndex;
    static constexpr std::size_t new_index = NewIndex;
};

template<class... Leaves>
struct index_map : Leaves... {};

给定一个正确构建的index_map,从旧索引到新索引的转换很简单,利用模板参数推导和重载解析:

template<std::size_t OldIndex, std::size_t NewIndex, class T>
index_map_leaf<T, OldIndex, NewIndex> 
    do_convert_index(index_map_leaf<T, OldIndex, NewIndex>);

template<std::size_t OldIndex, class IndexMap>
using converted_index_t = decltype(do_convert_index<OldIndex>(IndexMap()));

converted_index_t<OldIndex, IndexMap>::new_index就是新索引。

要构建索引图,我们分三步完成。我们首先将类型转换为类型 - 索引对。

template<class... Ts, std::size_t... Is>
typelist<index_map_leaf<Ts, Is, 0>...>
    do_build_old_indices(typelist<Ts...>, std::index_sequence<Is...>);

template<class TL>
using build_old_indices =
    decltype(do_build_old_indices(TL(),  std::make_index_sequence<TL::size>()));

接下来,我们对这些对进行分区。我们需要一个元函数,它将另一个元函数应用于它的参数&#39;嵌套的typedef type而不是参数本身。

// Given a metafunction, returns a metafunction that applies the metafunction to
// its arguments' nested typedef type.
template<class F>
struct project_type {
    template<class... Args>
    using apply = typename F::template apply<typename Args::type...>;
};

鉴于此,分割typelist index_map_leaf只是partition_t<LeafList, project_type<F>>

最后,我们转换分区列表,添加新索引。

template<class... Ts, std::size_t... Is, std::size_t...Js>
typelist<index_map_leaf<Ts, Is, Js>...> 
    do_build_new_indices(typelist<index_map_leaf<Ts, Is, 0>...>,
                         std::index_sequence<Js...>);

template<class TL>
using build_new_indices =
   decltype(do_build_new_indices(TL(), std::make_index_sequence<TL::size>()));

将所有这些结合起来,

template<class TL, class F>
using make_index_map = 
    apply_t<quote<index_map>, build_new_indices<partition_t<build_old_indices<TL>, 
                                                            project_type<F>>>>;

使用一个小实用程序将任意模板的参数转换为类型列表:

template<template<class...> class T, class... Args>
typelist<Args...> do_as_typelist(typelist<T<Args...>>);

template<class T>
using as_typelist = decltype(do_as_typelist(typelist<T>()));

我们只能通过直接从index_map构造重新排序的元组类型来进行一次分区。

template<class Tuple, class F>
struct tuple_partitioner {
    using map_type = make_index_map<as_typelist<Tuple>, F>;
    using reordered_tuple_type = apply_t<project_type<quote<std::tuple>>,
                                         as_typelist<map_type>>;
    template<std::size_t OldIndex>
    using new_index_for = 
        std::integral_constant<std::size_t,
                               converted_index_t<OldIndex, map_type>::new_index>;
};

例如,给定

using original_tuple = std::tuple<int, double, long, float, short>;
using f = quote<std::is_integral>;
using partitioner = tuple_partitioner<original_tuple, f>;

以下断言成立:

static_assert(partitioner::new_index_for<0>() == 0, "!");
static_assert(partitioner::new_index_for<1>() == 3, "!");
static_assert(partitioner::new_index_for<2>() == 1, "!");
static_assert(partitioner::new_index_for<3>() == 4, "!");
static_assert(partitioner::new_index_for<4>() == 2, "!");
static_assert(std::is_same<partitioner::reordered_tuple_type,
                           std::tuple<int, long, short, double, float>>{}, "!");

Demo

P.S。这是我的filter版本:

template<typename A, typename F>
using filter_one = std::conditional_t<F::template apply<A>::value,
                                      typelist<A>, typelist<>>;

template<typename F, typename... Args>
concat_t<filter_one<Args, F>...> do_filter(typelist<Args...>);

template <typename TL, typename F>
using filter_t = decltype(do_filter<F>(TL()));