2015-03-24 19 views
5

Dato uno boost::tuple e std::tuple, come si converte tra loro?Conversione tra std :: tuple e boost :: tuple

In altre parole, come implementeresti le seguenti due funzioni?

template <typename... T> boost::tuple<T...> asBoostTuple( std::tuple<T...> stdTuple); 
template <typename... T> std::tuple<T...> asStdTuple (boost::tuple<T...> boostTuple); 

Sembra essere una cosa semplice, ma non sono riuscito a trovare alcuna soluzione valida.


Cosa ho provato?

Ho finito per risolverlo con la programmazione dei modelli. Sembra che funzioni nella mia configurazione concreta, ma non mi sembra giusto (troppo prolisso), e non sono nemmeno sicuro se funzioni effettivamente in tutte le situazioni (arriverò a quel punto più tardi).

Comunque, ecco la parte interessante:

template<int offset, typename... T> 
struct CopyStdTupleHelper 
{ 
    static void copy(boost::tuple<T...> source, std::tuple<T...>& target) { 
     std::get<offset>(target) = std::move(boost::get<offset>(source)); 
     CopyStdTupleHelper<offset - 1, T...>::copy(source, target); 
    } 

    static void copy(std::tuple<T...> source, boost::tuple<T...>& target) { 
     boost::get<offset>(target) = std::move(std::get<offset>(source)); 
     CopyStdTupleHelper<offset - 1, T...>::copy(source, target); 
    } 
}; 

template<typename... T> 
struct CopyStdTupleHelper<-1, T...> 
{ 
    static void copy(boost::tuple<T...> source, std::tuple<T...>& target) 
     { /* nothing to do (end of recursion) */ } 

    static void copy(std::tuple<T...> source, boost::tuple<T...>& target) 
     { /* nothing to do (end of recursion) */ } 
}; 


std::tuple<T...> asStdTuple(boost::tuple<T...> boostTuple) { 
    std::tuple<T...> result; 
    CopyStdTupleHelper<sizeof...(T) - 1, T...>::copy(std::move(boostTuple), result); 
    return result; 
} 

boost::tuple<T...> asBoostTuple(std::tuple<T...> stdTuple) { 
    boost::tuple<T...> result; 
    CopyStdTupleHelper<sizeof...(T) - 1, T...>::copy(std::move(stdTuple), result); 
    return result; 
} 

Mi chiedo se c'è un modo più elegante. Sembra un'operazione molto comune per racchiudere le API esistenti utilizzando boost::tuple a std::tuple.


ho cercato di fornire un esempio di test minimale, dove solo l'attuazione di asBoostTuple e asStdTuple manca. Tuttavia, per un po 'di magia con boost::tuples::null_type, che non comprendo completamente, non riesce a compilare. Questo è anche il motivo per cui non sono sicuro che la mia soluzione esistente possa essere generalmente applicata.

Ecco il frammento:

#include <tuple> 
#include <boost/tuple/tuple.hpp> 

template <typename... T> 
boost::tuple<T...> asBoostTuple(std::tuple<T...> stdTuple) { 
    boost::tuple<T...> result; 
    // TODO: ... 
    return result; 
} 

template <typename... T> 
std::tuple<T...> asStdTuple(boost::tuple<T...> boostTuple) { 
    std::tuple<T...> result; 
    // TODO: ... 
    return result; 
} 

int main() { 
    boost::tuple<std::string, int, char> x = asBoostTuple(std::make_tuple("A", 1, 'x')); 

    // ERROR: 
    std::tuple<std::string, int, char> y = asStdTuple<std::string, int, char>(x); 

    return x == y; 
} 

Il messaggio di errore è:

example.cpp:20:38: error: no viable conversion from 'tuple<[3 * 
     ...], boost::tuples::null_type, boost::tuples::null_type, 
     boost::tuples::null_type, boost::tuples::null_type, 
     boost::tuples::null_type, boost::tuples::null_type, 
     boost::tuples::null_type>' to 'tuple<[3 * ...], (no 
     argument), (no argument), (no argument), (no argument), 
     (no argument), (no argument), (no argument)>' 
    ...int, char> y = asStdTuple<std::string, int, char>(x); 

Capisco implementazione che di spinta non si basa su modelli variadic, che dovrebbe spiegare il null_type, ma ancora' Non sono sicuro di come evitare questo errore di compilazione.

risposta

10

Il solito trucco quando si esegue questo tipo di manipolazioni consiste nel costruire una sequenza intera e quindi utilizzare un'espansione di pacchetto per inizializzare la nuova tupla.

La torsione extra in questo caso è null_type. Per questo è probabilmente più semplice trattare il tipo di tupla come opaco e manipolarlo usando boost::tuples::length e boost::tuples::element, che già gestisce correttamente null_type. In questo modo non ti affidi ai dettagli di implementazione delle tuple boost.

Quindi:

template <typename StdTuple, std::size_t... Is> 
auto asBoostTuple(StdTuple&& stdTuple, std::index_sequence<Is...>) { 
    return boost::tuple<std::tuple_element_t<Is, std::decay_t<StdTuple>>...> 
         (std::get<Is>(std::forward<StdTuple>(stdTuple))...); 
} 

template <typename BoostTuple, std::size_t... Is> 
auto asStdTuple(BoostTuple&& boostTuple, std::index_sequence<Is...>) { 
    return std::tuple<typename boost::tuples::element<Is, std::decay_t<BoostTuple>>::type...> 
        (boost::get<Is>(std::forward<BoostTuple>(boostTuple))...); 
} 

template <typename StdTuple> 
auto asBoostTuple(StdTuple&& stdTuple) { 
    return asBoostTuple(std::forward<StdTuple>(stdTuple), 
      std::make_index_sequence<std::tuple_size<std::decay_t<StdTuple>>::value>()); 
} 

template <typename BoostTuple> 
auto asStdTuple(BoostTuple&& boostTuple) { 
    return asStdTuple(std::forward<BoostTuple>(boostTuple), 
      std::make_index_sequence<boost::tuples::length<std::decay_t<BoostTuple>>::value>()); 
} 

Demo.

noti che la struttura di base del codice è esattamente identico per le due situazioni: si ottiene la dimensione della tupla (tramite boost::tuples::length o std::tuple_size), costruire una successione di interi utilizzando std::make_index_sequence, e quindi utilizzare la successione di interi avere la tipi con boost::tuples::element e std::tuple_element e i valori con boost::get/std::get.