如何可以任意排序元组的类型? [英] How can I arbitrarily sort a tuple's types?

查看:194
本文介绍了如何可以任意排序元组的类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有一件事让我真正感到厌烦的是,一个空的 struct / class 占用空间。 p>

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



因为这个原因,我需要一种基于某些标准重新排序类型的方法。基于大小排序整个列表不是必要的(在某些情况下可能是有害的),所以我需要一些通用的方法来重新排序元组的类型列表,但仍然访问它,如果类型列表是按原始顺序。 >

我环顾了一下,没有发现这样的东西,我失去了。如何实现这一点?



示例



  }; 
struct B {};
//需要根据一些条件重新排序。
std :: tuple< A,int,B,float> X;
//在这种情况下,将所有空对象移动在一起,如:
// std :: tuple< A,B,int,float> X;
//但是仍然有get< 1>(x)返回`int'并且得到< 2>(x)返回`B'。
static_assert(std :: is_same< decltype(get< 0>()),A> :: value,0应该是A
static_assert(std :: is_same< decltype(get< 1>()),int> :: value,1应该是int
static_assert(std :: is_same< decltype(get< 2>()),B> :: value,2应该是float
static_assert(std :: is_same< decltype(get< 3>()),float> :: value,3 should be type B);

这不能用手工处理的原因是,这可能是模板的一部分, tuple可以为空或不是,基于参数:

 模板< typename A,typename B,typename C,typename D> ; 
class X
{
//需要这个自动安排给出一些条件
//像大小或移动所有的空。
tuple< A,B,C,D> X;
public:
template< int i>
auto get() - > typename std :: tuple_element< i,decltype(x)>
{
return get< i>(x);
}
};
//这些类型是什么?谁知道。这可以埋在某处
//模板库。
X< T1,T2,T3,T4> X;基于Barry做了什么。



解决方案b
$ b


所以从这里我需要一个映射元函数来使用原来的
索引,我该怎么办?


首先,帮助索引映射。因为我很懒,我稍微修改了 typelist

  < 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 .. 。是> 
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>()

接下来,我们分割对。我们需要一个元函数,将另一个元函数应用到其参数的嵌套typedef 类型,而不是参数本身。

  //给定元函数,返回一个元函数,该元函数将元函数应用于
//其参数的嵌套typedef类型。
template< class F>
struct project_type {
template< class ... Args>
using apply = typename F :: template apply< typename Args :: type ...> ;;
};鉴于此,分区类型串 of code> index_map_leaf
只需 partition_t



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

  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< class ...>类T,类... Args> 
typelist< Args ...> do_as_typelist(typelist< T< Args ...>>);

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

我们只能通过直接从<$ c $构造重新排序的元组类型来执行一次分区c> index_map

  template< class Tuple,class F& 
struct tuple_partitioner {
using map_type = make_index_map< as_typelist< Tuple>,F> ;;
使用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>> {},!

演示






PS这是我的版本的过滤器

  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()));


One thing that really annoys me about C++ is that an empty struct/class takes up space.

So, I have this idea that std::tuple (or some variant, since it's (and the compiler's) implementation is highly implementation dependent) might be able to save the day, which it sort of does, but there are issues due to packing and alignment. Because of how compilers will align the items in the struct, having a empty next to a non-empty next to an empty next to a non-empty will be larger than 2 empties next to 2 non-empties.

Because of this, I need a way to reorder the types based on some criteria. Sorting the entire list based on size isn't necessary (and may in some cases be detrimental) so I need some generic way to reorder the tuple's type list but still access it as if the type list was in the original order.

I looked around a bit and haven't found anything like this and I'm at a loss. Ideas on how to accomplish this?

Example

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");

The reason this cannot be done by hand is that this could be part of a template and the elements in tuple may be empty or not, based on the parameters:

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;

解决方案

Building on what Barry did.

So from here I'd need a mapping meta-function to use the original indices, how would I do that?

First, some helpers to facilitate index mapping. And because I'm lazy, I modified typelist slightly.

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... {};

Given a properly built index_map, converting from old index to new index is simple, leveraging template argument deduction and overload resolution:

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 is, well, the new index.

To build the index map, we do it in in three steps. We start by transforming the types into type-index pairs.

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>()));

Next, we partition the pairs. We need a metafunction that applies another metafunction to its arguments' nested typedef type rather than the arguments themselves.

// 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...>;
};

Given this, partitioning a typelist of index_map_leafs is simply partition_t<LeafList, project_type<F>>.

Finally, we transform the partitioned list, adding the new indices.

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>()));

Bringing it all together,

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>>>>;


With a little utility to convert the arguments of an arbitrary template to a type list:

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>()));

We can do the partitioning only once, by constructing the reordered tuple type directly from the 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>;
};

For example, given

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

The following assertions hold:

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. Here's my version of 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()));

这篇关于如何可以任意排序元组的类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆