Вставить / удалить тип в список вариативных шаблонов (пакет параметров)

Как лучше всего реализовать вставку и удаление типа на основе индекса в списке типов вариативного шаблона (пакет параметров)?

Желаемый код / ​​поведение:

template<typename...> struct List { /* ... */ };

static_assert(is_same
<
    List<int, char, float>::Insert<int, 0>,
    List<int, int, char, float>
>());

static_assert(is_same
<
    List<int, char, float>::Insert<int, 2>,
    List<int, char, int, float>
>());

static_assert(is_same
<
    List<int, char, float>::Remove<0>,
    List<char, float>
>());

static_assert(is_same
<
    List<int, char, float>::Remove<1>,
    List<int, float>
>());

Я пробовал реализацию, основанную на возвращении аргументов в изначально пустой список, но его было очень трудно читать / поддерживать. Параметры были похожи на эти:

template<typename T, int I, int ITarget, typename TResult> struct InsertImpl;

Я постоянно увеличиваю I, пока он не станет равным ITarget, отодвигая существующие типы в TResult, что является List<...>. Когда I равно ITarget, я также возвращаю T в TResult.

Удаление типа имело аналогичную реализацию - вместо того, чтобы дважды возвращаться, когда индексы были равны, я просто пропустил тип.

Мое громоздкое решение будет реализовывать вставку и удаление с точки зрения нажатия и выталкивания. Я считаю, что было бы более элегантно иметь толкание вперед, равное Insert<0>, и толкание назад, равное Insert<size>. То же самое относится к появлению спереди и сзади.

Есть ли лучший способ сделать это? Могут ли помочь функции C ++ 14?


person Vittorio Romeo    schedule 10.02.2015    source источник
comment
Как насчет использования boost::mpl::vector для помощи в этих операциях?   -  person Pradhan    schedule 10.02.2015


Ответы (3)


Поскольку вы упомянули C ++ 14, вот еще один, использующий std::index_sequence. Основная причина, по которой, я думаю, стоит упомянуть это решение, - это использование constexpr функций сопоставления для размещения типов на их позициях в результирующем List. Это делает реализацию относительно простой.

#include <cstddef>
#include <tuple>
#include <utility>

template<typename...> struct List;

constexpr std::size_t map_ins(std::size_t i, std::size_t from, std::size_t to)
{
   return i < to ? i : i == to ? from : i - 1;
}

template<typename, typename, std::size_t, typename...> struct ins_hlp;

template<std::size_t... Is, typename U, std::size_t N, typename... Ts> 
struct ins_hlp<std::index_sequence<Is...>, U, N, Ts...>
{
   static_assert(N <= sizeof...(Ts), "Insert index out of range");
   using type = List<std::tuple_element_t<map_ins(Is, sizeof...(Ts), N), std::tuple<Ts..., U>>...>;
};

constexpr std::size_t map_rem(std::size_t i, std::size_t idx)
{
   return i < idx ? i : i + 1;
}

template<typename, std::size_t, typename...> struct rem_hlp_2;

template<std::size_t... Is, std::size_t N, typename... Ts> 
struct rem_hlp_2<std::index_sequence<Is...>, N, Ts...>
{
   using type = List<std::tuple_element_t<map_rem(Is, N), std::tuple<Ts...>>...>;
};

template<std::size_t N, typename... Ts> struct rem_hlp
{
   static_assert(N < sizeof...(Ts), "Remove index out of range");
   using type = typename rem_hlp_2<std::make_index_sequence<sizeof...(Ts) - 1>, N, Ts...>::type;
};

template<typename... Ts> struct List
{
   template<typename U, std::size_t N> using Insert = typename ins_hlp<std::make_index_sequence<sizeof...(Ts) + 1>, U, N, Ts...>::type;
   template<std::size_t N> using Remove = typename rem_hlp<N, Ts...>::type;
};

Извините за длинные строки, но я не нашел другого значимого способа форматирования этих списков аргументов.

Единственная причина наличия дополнительного помощника для Remove - проверка границ; если в этом нет необходимости, Remove может использовать тот же шаблон, что и Insert.

person bogdan    schedule 11.02.2015
comment
Очень чистое и интересное решение. Я до сих пор не очень хорошо знаком с такой реализацией, но я постараюсь изо всех сил разобраться в ней подробно. Спасибо! - person Vittorio Romeo; 12.02.2015

Не уверен, что есть лучший способ, но это нерекурсивный способ:

#include <utility>
#include <type_traits>
#include <tuple>

template<typename...Ts> struct List;

template<typename T> struct ListFromTupleImpl;
template<typename...Ts>
struct ListFromTupleImpl<std::tuple<Ts...>>
{ using type = List<Ts...>; };

template<typename T>
using ListFromTuple = typename ListFromTupleImpl<T>::type;

template<typename...Ts>
using TupleCat = decltype(std::tuple_cat(std::declval<Ts>()...));

template<typename...Ts>
using ListFromTupleCat = ListFromTuple<TupleCat<Ts...>>;

template<unsigned P,typename T,typename I> struct RemoveFromListImpl;
template<unsigned P,typename...Ts,std::size_t...Is>
struct RemoveFromListImpl<P,List<Ts...>,std::index_sequence<Is...>>
{
    using type = ListFromTupleCat<
       std::conditional_t<(Is==P),std::tuple<>,std::tuple<Ts>>...>;
};

// All elements < P
template<unsigned P,typename T,typename I> struct HeadImpl;
template<unsigned P,typename...Ts,std::size_t...Is>
struct HeadImpl<P,List<Ts...>,std::index_sequence<Is...>>
{
    using type = TupleCat<
       std::conditional_t<(Is>=P),std::tuple<>,std::tuple<Ts>>...>;
};

// All elements >= P
template<unsigned P,typename T,typename I> struct TailImpl;
template<unsigned P,typename...Ts,std::size_t...Is>
struct TailImpl<P,List<Ts...>,std::index_sequence<Is...>>
{
    using type = TupleCat<
       std::conditional_t<(Is<P),std::tuple<>,std::tuple<Ts>>...>;
};

template<typename N,unsigned P,typename T,typename I>
struct InsertIntoListImpl
{
    using head = typename HeadImpl<P,T,I>::type;
    using tail = typename TailImpl<P,T,I>::type;
    using type = ListFromTupleCat<head,std::tuple<N>,tail>;
};

template<typename...Ts> struct List {
    /* ... */
    template<std::size_t P>
    using Remove =
      typename RemoveFromListImpl<P,List<Ts...>,
        std::index_sequence_for<Ts...>>::type;

    template<typename N,std::size_t P>
    using Insert =
      typename InsertIntoListImpl<N,P,List<Ts...>,
        std::index_sequence_for<Ts...>>::type;
};


static_assert(std::is_same
<
    List<int, char, float>::Remove<0>,
    List<char, float>
>(), "");

static_assert(std::is_same
<
    List<int, char, float>::Remove<1>,
    List<int, float>
>(), "");

static_assert(std::is_same
<
    List<int, char, float>::Insert<int, 0>,
    List<int, int, char, float>
>(), "");

static_assert(std::is_same
<
    List<int, char, float>::Insert<int, 2>,
    List<int, char, int, float>
>(), "");

int main(){}

Живой пример

person Daniel Frey    schedule 10.02.2015

Используя крошечную библиотеку метапрограммирования Эрика Ниблера ( ДЕМО):

template <std::size_t N, typename List>
using take_c =
  meta::reverse<
    meta::drop_c<
      meta::size<List>::value - N,
      meta::reverse<List>
>>;

template <typename...Ts> struct List {
  using mlist = meta::list<Ts...>;

  template <typename T, std::size_t I>
  using Insert =
    meta::apply_list<
      meta::quote<::List>,
      meta::concat<
        take_c<I, mlist>,
        meta::list<T>,
        meta::drop_c<I, mlist>
  >>;

  template <std::size_t I>
  using Remove =
    meta::apply_list<
      meta::quote<::List>,
      meta::concat<
        take_c<I, mlist>,
        meta::drop_c<I + 1, mlist>
  >>;
};
person Casey    schedule 10.02.2015