2015-02-10 10 views
5

Qual è il modo migliore di implementare l'inserimento basato su indice e la cancellazione di un tipo in un elenco di tipi di template variadic (parameter pack)?Inserimento/rimozione del tipo nell'elenco dei modelli variadici (pacchetto parametri)

desiderata codice/comportamento:

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

ho provato un'applicazione basata sulle spingendo indietro gli argomenti in una lista inizialmente vuota, ma era molto difficile da leggere/mantenere. I parametri erano simili a questo:

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

ho costantemente incrementare I fino a quando non è uguale a ITarget, spingendo indietro tipi esistenti in TResult, che è un List<...>. Quando I è uguale a ITarget, spingo indietro T in TResult pure.

La rimozione di un tipo ha avuto un'implementazione simile - invece di respingere due volte quando gli indici erano uguali, ho semplicemente saltato il tipo.

La mia soluzione macchinosa implementerebbe l'inserimento e la rimozione in termini di spinta e scoppiettio. Credo che sarebbe più elegante spingere in avanti uguale a Insert<0> e spingere sul retro uguale a Insert<size>. Lo stesso vale per lo schiocco dalla parte anteriore e dalla parte posteriore.

C'è un modo migliore per farlo? Potrebbero essere utili le funzionalità di C++ 14?

+1

Come utilizzare l'utilizzo di 'boost :: mpl :: vector' per aiutare con queste operazioni? – Pradhan

risposta

1

Dal hai citato C++ 14, eccone un altro che fa uso di std::index_sequence. Il motivo principale per cui penso che la soluzione sia degna di nota è l'uso delle funzioni di mappatura constexpr per posizionare i tipi nelle loro posizioni nel risultante List. Ciò rende l'implementazione relativamente semplice.

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

Siamo spiacenti per le linee lunghe, ma non ho trovato un altro modo significativo per formattare tali elenchi di argomenti.

L'unico motivo per avere un helper aggiuntivo per Remove è il controllo dei limiti; se non è necessario, Remove può utilizzare lo stesso modello di Insert.

+0

Soluzione molto pulita e interessante. Non mi sento ancora molto familiare con questo tipo di implementazione, ma farò del mio meglio per capirlo in dettaglio. Grazie! –

3

Non che non v'è alcun modo "migliore", ma questo è un modo non ricorsivo:

#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(){} 

Live example

1

Utilizzando Eric Niebler's Tiny Meta-Programming Library (DEMO):

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