2015-03-24 3 views
5

Учитывая boost::tuple и std::tuple, как вы можете конвертировать между ними?Преобразование между std :: tuple и boost :: tuple

Другими словами, как бы вы реализовали следующие две функции?

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

Кажется, что это просто, но я не мог найти никаких хороших решений.


Что я пробовал?

Я закончил работу с шаблоном программирования. Кажется, что это работает в моей конкретной настройке, но это не кажется правильным (слишком многословным), и я даже не уверен, действительно ли он работает во всех ситуациях (я приду к этому вопросу позже).

Во всяком случае, вот интересная часть:

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

Интересно, если есть более элегантный способ. Похоже, что очень распространенная операция для переноса существующих API с использованием boost::tuple в std::tuple.


Я пытался предоставить вам с минимальным тестового примера, где не хватает только реализация asBoostTuple и asStdTuple. Однако для некоторой магии с boost::tuples::null_type, которую я не совсем понимаю, он не компилируется. Это также является причиной того, что я не уверен, что мое существующее решение может быть в целом применено.

Вот фрагмент кода:

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

Сообщение об ошибке:

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

Я понимаю, реализация, повышающая не основана на переменном число шаблонов, которые должны объяснить null_type, но все-таки я m не уверен, как избежать этой ошибки компиляции.

ответ

10

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

Дополнительный поворот в этом случае равен null_type. Для этого, вероятно, проще всего обработать тип кортежа как непрозрачный, и манипулировать им с помощью boost::tuples::length и boost::tuples::element, который уже обрабатывает null_type должным образом. Таким образом, вы не полагаетесь на детали реализации форсированных кортежей.

Итак:

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.

Следует отметить, что основная структура коды является абсолютно идентичной для двух ситуаций: мы получаем размер кортежа (через boost::tuples::length или std::tuple_size), построить целое последовательности с использованием std::make_index_sequence, а затем использовать целую последовательность для получения типы с boost::tuples::element и std::tuple_element, а значения - boost::get/std::get.

Смежные вопросы