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