简体   繁体   English

std :: tuple和boost :: tuple之间的转换

[英]Conversion between std::tuple and boost::tuple

Given a boost::tuple and std::tuple , how do you convert between them? 给定boost::tuplestd::tuple ,你如何在它们之间进行转换?

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 . 使用boost::tuple将现有API包装到std::tuple似乎是一种非常常见的操作。


I tried to provide you with a minimal test example, where only the implementation of asBoostTuple and asStdTuple is missing. 我试图为您提供一个最小的测试示例,其中仅缺少asBoostTupleasStdTuple的实现。 However, for some magic with boost::tuples::null_type , which I don't fully understand, it fails to compile. 但是,对于一些我不完全理解的boost::tuples::null_type魔法,它无法编译。 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. 我理解Boost的实现不是基于可变参数模板,它应该解释null_type ,但我仍然不确定如何避免编译错误。

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 . 在这种情况下,额外的扭曲是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. 为此,将元组类型视为不透明可能是最简单的,并使用boost::tuples::lengthboost::tuples::element来操作它,它已经正确处理了null_type This way you do not rely on the implementation details of boost tuples. 这样您就不必依赖boost元组的实现细节了。

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 . 请注意,代码的基本结构对于两种情况完全相同:我们获取元组的大小(通过boost::tuples::lengthstd::tuple_size ),使用std::make_index_sequence构造整数序列,以及然后使用整数序列来获取带有boost::tuples::elementstd::tuple_element ,以及带有boost::get / std::get

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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