按大小存储字段的结构

A structure that stores its fields by size

我想知道如何在 C++ 中执行以下操作:

考虑这些 类 :

C1 < C2 < C3 < ... < Cn, Ci < Cj表示sizeof(Ci) < sizeof(Cj)

我想要一个结构,它使用可变参数模板作为 Ci 的序列,

OrderBySize<AnySequenceOfCis>,例如:OrderBySize<C1,C2,C3,...,Cn>OrderBySize<C2,C1,C3,C4,...,Cn> ...所有可能的组合

并给出以下结构作为结果:

class result{
  Cn elem1;
  Cn-1 elem2;
  .
  .
  .
  C1 elemn;
}

我读了 this article,它展示了我们如何定义一个 Tuple<typename ...T>,然而,这是不同的,实现起来要困难得多,但非常有用。

编辑:

order_by_size<T1, ..., Tn> 包含 T1, ..., Tn

的有序组合的元组

但是我不希望用户知道我正在对字段进行排序,用户会像元组一样使用它。因此,为了访问这些字段,用户将使用 :

template<typename... Tn> get<size_t>(const MyStructure<Tn ...>& m) 获取第 size_t 个元素,该元素在新 tuple.

中具有其他索引

这可能不是最有效的实现(它使用类型的循环排列来确定具有最大大小的类型),并且可能包含错误,但整个思路应该很清楚。结果是 std::tuple,类型按大小降序排列。主要功能检查它是否真的有效(并且它在我的 gcc-4.8.2 上有效)。

#include <iostream>
#include <tuple>
#include <iomanip>

constexpr std::size_t max (std::size_t x, std::size_t y)
{
   return (x < y) ? y : x;
}

template <typename ... Ts>
struct max_size;

template < >
struct max_size < >
{
       static constexpr std::size_t result = 0;
};

template <typename T, typename ... Ts>
struct max_size <T, Ts...>
{
       static constexpr std::size_t result = max(sizeof(T), max_size<Ts...>::result);
};

template <typename R, typename ... Ts>
struct order_by_size_impl;

template <bool M, typename R, typename ... Ts>
struct order_by_size_helper;

template <typename ... Rs, typename T, typename ... Ts>
struct order_by_size_helper<true, std::tuple<Rs...>, T, Ts...>
    : order_by_size_impl<std::tuple<Rs..., T>, Ts...>
{ };

template <typename ... Rs, typename T, typename ... Ts>
struct order_by_size_helper<false, std::tuple<Rs...>, T, Ts...>
    : order_by_size_impl<std::tuple<Rs...>, Ts..., T>
{ };

template <typename ... Rs, typename T, typename ... Ts>
struct order_by_size_impl<std::tuple<Rs...>, T, Ts...>
    : order_by_size_helper<sizeof(T) >= max_size<Ts...>::result, std::tuple<Rs...>, T, Ts...>
{ };

template <typename ... Rs>
struct order_by_size_impl<std::tuple<Rs...>>
{
       typedef std::tuple<Rs...> result;
};

template <typename ... Ts>
struct order_by_size
    : order_by_size_impl<std::tuple<>, Ts...>
{ };

struct test
{
   std::uint8_t data[128];
};

template <std::size_t I, typename T, typename R>
bool check (R const & r)
{
   return std::is_same<typename std::remove_cv<typename std::remove_reference<decltype(std::get<I>(r))>::type>::type, T>::value;
}

int main ( )
{
   order_by_size<std::uint8_t, std::uint32_t, std::uint16_t, std::uint64_t, test>::result r;
   std::cout << std::boolalpha;
   std::cout << check<0, test>(r) << std::endl;
   std::cout << check<1, std::uint64_t>(r) << std::endl;
   std::cout << check<2, std::uint32_t>(r) << std::endl;
   std::cout << check<3, std::uint16_t>(r) << std::endl;
   std::cout << check<4, std::uint8_t>(r) << std::endl;
}

基本上这个问题简化为仅根据给定的比较器对类型列表进行排序。一旦你有了它,其他一切都会随之而来。所以这个答案只是排序部分。我们将从类型列表开始:

template <typename...>
struct typelist {
    using type = typelist;
};

我将假设一堆非常短的元函数(headtailconcatsize)。为了简洁起见,我将省略它们。

那么让我们开始编写合并排序:

template <typename TL, typename Cmp = LessSize>
struct sort
{
    using left_right = typename split<TL, size<TL>::value/2>::type;
    using left = typename sort<head_t<left_right>, Cmp>::type;
    using right = typename sort<head_t<tail_t<left_right>>, Cmp>::type;

    using type = typename merge<left, right, Cmp>::type;
};

// base case for exactly 1 element
template <typename T, typename Cmp>
struct sort<typelist<T>, Cmp> {
    using type = typelist<T>;
};

// potentially add a base case for exactly 2 elements here?

这里的总体结构看起来应该很眼熟。我们将我们的类型列表 TL 分成两个相等的部分,对两者进行排序,然后合并。当然,这是元编程,所以一切都不必要地复杂。

让我们从 split 开始。 split 接受一个类型列表和一个大小,returns 一个包含两个类型列表的类型列表:第一个具有给定的大小,第二个是余数:

template <typename A, typename B, size_t N>
struct split_impl
    : std::conditional<
        size<A>::value < N,
        split_impl<concat_t<A, typelist<head_t<B>>>, tail_t<B>, N>,
        typelist<A, B>
        >::type
{ };

template <typename TL, size_t N>
struct split
    : split_impl<typelist<>, TL, N>
{ };

所以这给了我们 leftright(至少我们应用了一次 head_t<>head_t<tail_t<>>)。剩下的就是 merge 步骤。我正在使用元函数 class 是什么的 Boost MPL 想法,所以 LessSize 是:

struct LessSize {
    template <typename A, typename B>
    using apply = std::integral_constant<bool, sizeof(A) < sizeof(B)>;
};

merge 只需遍历两个类型列表并根据两个类型列表之间的比较器选择最小的元素。首先,我们将从所有基本案例开始:

template <typename L, typename R, typename Cmp>
struct merge;

// R empty
template <typename... T, typename Cmp>
struct merge<typelist<T...>, typelist<>, Cmp> {
    using type = typelist<T...>;
};

// L empty
template <typename... T, typename Cmp>
struct merge<typelist<>, typelist<T...>, Cmp> {
    using type = typelist<T...>;
};

然后是递归步骤,有点难看:

template <typename A, typename... As, typename B, typename... Bs, typename Cmp> 
struct merge<typelist<A, As...>, typelist<B, Bs...>, Cmp>
: std::conditional<
        Cmp::template apply<A, B>::value,
        concat_t<typelist<A>, typename merge<typelist<As...>, typelist<B, Bs...>, Cmp>::type>,
        concat_t<typelist<B>, typename merge<typelist<A, As...>, typelist<Bs...>, Cmp>::type>
        >::type
{ };

基本上,给定两个类型列表,{A, As...}{B, Bs...},我们 select 基于 Cmp 的最小类型列表,这就是我们要弹出元素的一面的。如果 Cmp::apply<A,B>,那么我们将 A{As...}{B, Bs...} 合并的结果连接起来。反之亦然。

这就是她写的全部内容:

template <typename T>
struct TD;

int main()
{
    using T = sort<typelist<int, double, char, float>, LessSize>::type;
    TD<T> r;
}

main.cpp: In function 'int main()':
main.cpp:131:11: error: aggregate 'TD<typelist<char, float, int, double> > r' has incomplete type and cannot be defined
     TD<T> r;
           ^

一旦你有了排序的类型,制作元组就很简单了:

template <template <typename...> class C>
struct meta_quote {
    template <typename... T>
    using apply = C<T...>;
};

template <typename F, typename TL>
struct meta_apply;

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

template <typename... T>
struct my_tuple
: meta_apply<meta_quote<std::tuple>,
             typename sort<typelist<T...>>::type
             >::type;
{ 
    using base_tuple = meta_apply<...>;
};

现在只需在 my_tuple<T...> 上为 get<> 添加重载:

template <size_t I, typename... T>
auto get(my_tuple<T...>& t) {
    using type = std::tuple_element_t<I, std::tuple<T...>>;
    return std::get<type>(static_cast<typename my_tuple<T...>::base_type&>(t));
}