c++ 将类型列表的编译时列表转换为索引列表的列表

bcs8qyzn  于 2023-05-02  发布在  其他
关注(0)|答案(3)|浏览(104)

我想把一个类型列表的编译时列表转换成一个std::integral_constant索引列表的列表。显然,这需要某种类似2d的变换。
示例来源

template<typename... Ts> struct type_list{};
using L = type_list<
    type_list<>,
    type_list<>,
    type_list<>,
    type_list<>,
    type_list<int>,
    type_list<>,
    type_list<int>,
    type_list<int>,
    type_list<int>,
    type_list<int,double>,
    type_list<int,double>
>;

预期结果

template<uint64_t V> using I = std::integral_constant<uint64_t, V>; // alias for better legibility
using R = type_list<
    type_list<>,
    type_list<>,
    type_list<>,
    type_list<>,
    type_list<I<0>>,
    type_list<>,
    type_list<I<1>>,
    type_list<I<2>>,
    type_list<I<3>>,
    type_list<I<4>, I<5>>,
    type_list<I<6>, I<7>>
>;

我认为boost::mp11完全符合这个目的,但我缺乏将这一切结合在一起的经验。请分享您的建议。

wlwcrazw

wlwcrazw1#

我认为boost::mp 11完全符合这个目的,但我缺乏将这一切结合在一起的经验。请分享您的建议。
你说得对!Boost.Mp11确实非常适合这个目的。
这基本上是一个有状态的折叠。我们必须记录到目前为止我们已经推送了多少个值,称之为N。然后,对于我们正在迭代的列表中的每个新元素L,我们附加一个新的列表[N, N+1, ..., N+len(L)-1],然后在状态中执行N += len(L)
为了方便起见,我们可以添加一个State

template <class N, class L>
struct State {
    using value = N;
    using list = L;
};

一个累加函数,它执行我上面描述的操作:

template <class S, class L>
using F = State<
    // S::value + len(L)
    mp_plus<typename S::value, mp_size<L>>,
    mp_push_back<typename S::list,
        // iota just gives you [0, 1, ..., V-1]
        // so we need to add S::value to all of those
        mp_transform_q<
            mp_bind_front<mp_plus, typename S::value>,
            mp_iota<mp_size<L>>>>
    >;

最后,我们执行fold,并拉出list

template <class L>
using types_to_ints = mp_fold<L, State<mp_size_t<0>, mp_list<>>, F>::list

Example证明它做了正确的事情。

but5z9lq

but5z9lq2#

下面是一个使用纯C++20的解决方案。
首先是一个小助手,用于将类型前置到类型列表中:

template<class T, class List> struct prepend_type_list;

template<class T, class... Args>
struct prepend_type_list<T, type_list<Args...>> 
: std::type_identity<type_list<T, Args...>> {};

我们的主要切入点是这样的:

template <class List, std::size_t Idx = 0>
struct index;

我们将递归地对列表进行索引,因此需要一个List为空的基本情况:

template <std::size_t Idx>
struct index<type_list<>, Idx> : std::type_identity<type_list<>> {};

现在是递归专门化。由于我们有两个递归级别(外部列表和内部列表),我们将编写一个index_inner助手,它执行内部索引,并返回它使用的最后一个索引:

template <class Head, class... Tail, std::size_t Idx>
struct index<type_list<Head, Tail...>, Idx> {
    using inner = index_inner<Head, type_list<>, Idx>;
    using type = typename prepend_type_list<
                                typename inner::type, 
                                typename index<type_list<Tail...>, inner::index>::type
                          >::type;
};

index_inner的主模板,它不是用尾递归构建,而是在运行时构建列表,这样它就可以跟踪索引(你也可以用尾递归来构建,然后反转列表):

template <class List, class Build, std::size_t Idx>
struct index_inner;

完成后,我们将公开max索引和构建的类型列表:

template <class Build, std::size_t Idx>
struct index_inner<type_list<>, Build, Idx> {
    using type = Build;
    static constexpr std::size_t index = Idx;
};

最后,递归的情况:

template <class Head, class... Tail, class... Build, std::size_t Idx>
struct index_inner<type_list<Head, Tail...>, type_list<Build...>, Idx> 
: index_inner<type_list<Tail...>, type_list<Build..., I<Idx>>, Idx+1> {};

然后我们可以使用index<L>::type得到R
现场演示:https://godbolt.org/z/KT1dEMs5q

cngwdvgl

cngwdvgl3#

这里有另一种选择

#include <ranges>
#include <algorithm>
#include <array>
#include <vector>
#include <numeric>

template <typename... Ts> 
constexpr auto type_list_to_vector(type_list<Ts...>) {
  return std::vector<int>(sizeof...(Ts));
}

template <auto arr> 
constexpr auto arr_to_type_list() {
  return []<std::size_t... Is>(std::index_sequence<Is...>) {
    return type_list<I<arr[Is]>...>{};
  }(std::make_index_sequence<arr.size()>{});
}

template <typename... Ts> 
constexpr auto transform(type_list<Ts...>) {
  auto v = [] {
    std::vector<std::vector<int>> v{type_list_to_vector(Ts{})...};
    std::ranges::iota(v | std::views::join, 0);
    return v;
  };
  return [&]<std::size_t... Xs>(std::index_sequence<Xs...>) {
    return type_list<decltype([&] {
      constexpr auto arr = [&] {
        std::array<int, v()[Xs].size()> arr{};
        std::ranges::copy(v()[Xs], arr.begin());
        return arr;
      }();
      return arr_to_type_list<arr>();
    }())...>{};
  }(std::index_sequence_for<Ts...>{});
}

基本思想是转换以下内容:

using L = type_list<
  type_list<>,
  type_list<>,
  type_list<int>,
  type_list<>,
  type_list<int>,
  type_list<int,double>
>;

进入:

std::vector<std::vector<int>> v{{}, {}, {0}, {}, {0}, {0, 0}};

然后应用算法:

ranges::iota(v | views::join, 0);

获得:

std::vector<std::vector<int>> v{{}, {}, {0}, {}, {1}, {2, 3}};

最后将结果转换回

using L = type_list<
  type_list<>,
  type_list<>,
  type_list<I<0>>,
  type_list<>,
  type_list<I<1>>,
  type_list<I<2>,I<3>>
>;

Demo

相关问题