简体   繁体   中英

Conversion between std::tuple and boost::tuple

Given a boost::tuple and std::tuple , how do you convert between them?

In other words, how would you implement the following two functions?

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

Seems to be a straightforward thing, but I could not find any good solutions.


What did I try?

I ended up solving it with template programming. It seems to work in my concrete setup, but it does not feel right (too verbose), and I'm even not sure if it actually works in all situations (I'll come to that point later).

Anyway, here is the interesting part:

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

I wonder if there is a more elegant way. It seems like a very common operation to wrap existing APIs using boost::tuple to std::tuple .


I tried to provide you with a minimal test example, where only the implementation of asBoostTuple and asStdTuple is missing. However, for some magic with boost::tuples::null_type , which I don't fully understand, it fails to compile. That is also the reason why I'm not sure that my existing solution can be generally applied.

Here is the snippet:

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

The error message is:

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

I understand that Boost's implementation is not based on variadic templates, which should explain the null_type , but still I'm not sure how to avoid that compile error.

The usual trick when doing this sort of manipulations is to construct an integer sequence and then use a pack expansion to initialize the new tuple.

The extra twist in this case is null_type . For that it's probably simplest to treat the tuple type as opaque, and manipulate it using boost::tuples::length and boost::tuples::element , which already handles null_type properly. This way you do not rely on the implementation details of boost tuples.

So:

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 .

Note that the basic structure of the code is exactly identical for the two situations: we obtain the size of the tuple (via boost::tuples::length or std::tuple_size ), construct an integer sequence using std::make_index_sequence , and then use the integer sequence to obtain the types with boost::tuples::element and std::tuple_element , and the values with boost::get / std::get .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM