2013-07-14 11 views

risposta

6

Presumo che tu voglia tutto questo in fase di compilazione.

Ecco la spiegazione generale: concatenando tuple è simile a concatenare liste o matrici, è che l'algoritmo è lo stesso. Qui, date le tuple a e b, ho scelto di spostare l'ultimo elemento di a all'inizio di b e ripetere fino a quando a è vuoto.

Primo: strutture di base. La seguente struttura mantiene un pacchetto di parametri. Può essere qualsiasi cosa, ad esempio, una tupla:

template<typename... T> 
struct pack 
{ 
    static const unsigned int size = sizeof...(T); 
}; 

Si noti che la dimensione del pacchetto è memorizzato al suo interno. Non è obbligatorio, ma è conveniente per la spiegazione. Boost utilizza la struct boost::tuples::length<T>::value (che è più dettagliata).

Per accedere a un elemento nella posizione i-esima, si usa una struttura simile a boost::tuples::element<n, T>:

// Get i-th element of parameter pack 
// AKA 'implementation' 
// Principle: the element i is the first element of the sub-array starting at indice i-1 
template<int n, typename F, typename... T> 
struct element_at : public element_at<n-1, T...> 
{ 
}; 

template<typename F, typename... T> 
struct element_at<0, F, T...> 
{ 
    typedef F type; 
}; 

// Get i-th element of pack 
// AKA 'interface' for the 'pack' structure 
template<int n, typename P> 
struct element 
{ 
}; 

template<int n, typename... T> 
struct element<n, pack<T...>> 
{ 
    typedef typename element_at<n, T...>::type type; 
}; 

Ora, dobbiamo utilizzare un'operazione di basso livello che è l'aggiunta di un elemento ad un lato di un pacco (aggiungendo a sinistra o a destra). Qui aggiungendo a sinistra è scelto, ma non è l'unica scelta:

// Concat at left (only for structure 'pack') 
template<typename a, typename b> 
struct tuple_concat_left 
{ 
}; 

template<typename a, typename... b> 
struct tuple_concat_left<a, pack<b...>> 
{ 
    typedef pack<a, b...> type; 
}; 

Per i modelli, a non è cambiato, e invece usiamo un indice di sapere quale elemento da aggiungere. L'ereditarietà definisce un typedef 'tipo' che è la concatenazione di tutti gli indici dopo n e l'altra tupla (escluso n e in ordine). Dobbiamo solo concatenare a sinistra l'elemento sull'indice n.

// Concat 2 tuples 
template<typename a, typename b, int n = 0, bool ok = (n < a::size)> 
struct tuple_concat : public tuple_concat<a, b, n+1> 
{ 
    typedef typename tuple_concat_left< 
     typename element<n, a>::type, 
     typename tuple_concat<a, b, n+1>::type 
    >::type type; 
}; 

template<typename a, typename b, int n> 
struct tuple_concat<a, b, n, false> 
{ 
    typedef b type; 
}; 

E questo è tutto! Live example here.

Ora, per le specifiche della tupla: hai notato che non ho usato boost :: tuple né std :: tuple. Questo perché molte implementazioni di tuple boost non hanno accesso ai modelli variadici, quindi viene utilizzato un numero fisso di parametri del modello (sono predefiniti su boost::tuples::null_type). Mettere questo direttamente con i modelli variadici è un mal di testa, quindi la necessità di avere un'altra astrazione.

Suppongo anche che sia possibile utilizzare C++ 11 (con lo decltype nella domanda). Concatenare 2 tuple in C++ 03 è possibile, ma più ripetitivo e noioso.

È possibile convertire un pack ad una tupla molto facilmente: basta cambiare la definizione pack a:

template<typename... T> 
struct pack 
{ 
    static const unsigned int size = sizeof...(T); 
    typedef boost::tuple<T...> to_tuple; // < convert this pack to a boost::tuple 
}; 
1

C++ 14 offre una libreria per generare una sequenza di numeri interi a tipo di compilazione. Questo aiuta a manipolare sequenze statiche come tuple e array (example). Una successione di interi può essere ottenuto

template<size_t... Ints> 
struct integer_sequence {}; 

template<size_t Size, size_t... Ints> 
struct implementation : implementation<Size-1, Size-1, Ints...> {}; 

template<size_t... Ints> 
struct implementation<0, Ints...> 
{ 
    typedef integer_sequence<Ints...> type; 
}; 

template<class... T> 
using index_sequence_for = typename implementation<sizeof...(T)>::type; 

Per concat MyTuple e MyType è possibile scrivere le semplici funzioni:

template<typename X, typename Tuple, size_t... Ints> 
auto concat(X x, Tuple t, integer_sequence<Ints...>) 
    -> decltype(std::make_tuple(x, std::get<Ints>(t)...)) 
{ 
    return std::make_tuple(x, std::get<Ints>(t)...); 
} 

template<typename X, typename... T> 
std::tuple<X, T...> concat(X x, std::tuple<T...> t) 
{ 
    return concat(x, t, index_sequence_for<T...>()); 
} 

concat(MyType, MyTuple); 
Problemi correlati