struct to / from std :: tuple conversion

时间:2016-07-25 06:44:17

标签: c++ c++17

假设我structstd::tuple具有相同的类型布局:

struct MyStruct { int i; bool b; double d; }
using MyTuple = std::tuple<int,bool,double>;

是否有任何标准化的方式将一个人投入另一个?

P.S。我知道琐碎的内存复制可以做到这一点,但它是对齐和实现相关的

4 个答案:

答案 0 :(得分:10)

我们可以使用结构化绑定将结构转换为具有一些功能的元组。

struct-to-tuple非常尴尬。

template<std::size_t N>
struct to_tuple_t;

template<>
struct to_tuple_t<3> {
  template<class S>
  auto operator()(S&& s)const {
    auto[e0,e1,e2]=std::forward<S>(s);
    return std::make_tuple(e0, e1, e2);
  }
};

现在,为您要支持的每个尺寸写一个to_tuple_t。这很乏味。可悲的是,我知道无法在那里引入参数包。

template<std::size_t N, class S>
auto to_tuple(S&& s) {
  return to_tuple_t<N>{}(std::forward<S>(s));
}

我知道无法计算N所需的值。因此,当您调用它时,您必须在3中输入auto t = to_tuple<3>(my_struct);

我不是结构化绑定的大师。可能有&&&或decltype可以在这些行上完美转发:

    auto[e0,e1,e2]=std::forward<S>(s);
    return std::make_tuple(e0, e1, e2);

但如果没有编译器可以使用,我会保守并制作冗余副本。

将元组转换为结构很容易:

template<class S, std::size_t...Is, class Tup>
S to_struct( std::index_sequence<Is...>, Tup&& tup ) {
  using std::get;
  return {get<Is>(std::forward<Tup>(tup))...};
}
template<class S, class Tup>
S to_struct( Tup&&tup ) {
  using T=std::remove_reference_t<Tup>;

  return to_struct(
    std::make_index_sequence<std::tuple_size<T>{}>{},
    std::forward<Tup>(tup)
  );
}

基于tuple_size的SFINAE支持可能对to_struct有用。

上述代码适用于所有元组,例如std::pairstd::array以及您自定义代码以支持结构化绑定的任何内容(tuple_sizeget<I>)。

有趣的是,

std::array<int, 3> arr{1,2,3};
auto t = to_tuple<3>(arr);

工作并返回一个包含3个元素的元组,因为to_tuple基于结构化绑定,它与元组喜欢一起作为输入。

to_array是这个家庭的另一种可能性。

答案 1 :(得分:6)

不幸的是,没有自动的方法可以做到这一点,但另一种方法是将结构调整为Boost.Fusion序列。您可以为每个新班级一劳永逸地完成此任务。

#include <boost/fusion/adapted/struct/adapt_struct.hpp>
...
struct MyStruct { int i; bool b; double d; }

BOOST_FUSION_ADAPT_STRUCT(
    MyStruct,
    (int, i)
    (bool, b)
    (double, d)
)

使用MyStruct就好像它在Fusion.Sequence(它几乎适合你已经使用过的任何地方std::tuple<...>),如果你使这些功能通用。)作为奖励你不需要复制你的数据成员。

如果真的需要转换为std::tuple,在“Fusion-adaptting”之后你可以这样做:

#include <boost/fusion/adapted/std_tuple.hpp>
#include <boost/fusion/algorithm/iteration/for_each.hpp>
#include <boost/fusion/algorithm/transformation/zip.hpp>
...
auto to_tuple(MyStruct const& ms){
   std::tuple<int, bool, double> ret;
   auto z = zip(ret, ms);
   boost::fusion::for_each(z, [](auto& ze){get<0>(ze) = get<1>(ze);});
   // or use boost::fusion::copy
   return ret;
}

事实是std::tuple是半支持的功能。这就像拥有STD容器而没有算法。幸运的是,我们#include <boost/fusion/adapted/std_tuple.hpp>允许我们做出惊人的事情。

完整代码:

通过包含来自Boost.Fusion std_tuple.hpp的{​​{1}}标头自动适应Boost.Fusion序列,因此通过使用Boost.Fusion作为结构和{{之间的桥梁,可以实现以下内容: 1}}:

std::tuple

不会推荐std::tuple,因为这会导致投票结果为负,而且不可移植。

答案 2 :(得分:3)

  

是否有任何标准化的方式将一个人投入另一个?

无法&#34;演员&#34;一个到另一个。

最简单的方法可能是使用std::tie将元组打包到struct;

struct MyStruct { int i; bool b; double d; };
using MyTuple = std::tuple<int,bool,double>;

auto t = std::make_tuple(42, true, 5.1);
MyStruct s;

std::tie(s.i, s.b, s.d) = t;

Demo

您可以进一步将其包装在更高级别的宏或&#34; generator&#34; (make style)函数,例如

std::tuple<int, bool, double> from_struct(MyStruct const& src)
{
  return std::make_tuple(src.i, src.b, src.d);
}

MyStruct to_struct(std::tuple<int, bool, double> const& src)
{
  MyStruct s;
  std::tie(s.i, s.b, s.d) = src;
  return s;
}
  

我知道琐碎的内存复制可以做到这一点,但它是对齐和实现依赖吗?

你提到了&#34;平凡的记忆副本&#34;可以工作 - 仅用于复制个人成员。所以基本上,memcpy的整个结构的tuple反之亦然,并不总是像你期望的那样(如果有的话); tuple的内存布局未标准化。如果确实有效,则高度依赖于实施。

答案 3 :(得分:1)

struct转换的元组是微不足道的,但我认为在目前的C ++级别上是不可能的。

#include <type_traits>
#include <utility>

#include <tuple>

namespace details
{

template< typename result_type, typename ...types, std::size_t ...indices >
result_type
make_struct(std::tuple< types... > t, std::index_sequence< indices... >) // &, &&, const && etc.
{
    return {std::get< indices >(t)...};
}

}

template< typename result_type, typename ...types >
result_type
make_struct(std::tuple< types... > t) // &, &&, const && etc.
{
    return details::make_struct< result_type, types... >(t, std::index_sequence_for< types... >{}); // if there is repeated types, then the change for using std::index_sequence_for is trivial
}

#include <cassert>
#include <cstdlib>

int main()
{
    using S = struct { int a; char b; double c; };
    auto s = make_struct< S >(std::make_tuple(1, '2', 3.0));
    assert(s.a == 1);
    assert(s.b == '2');
    assert(s.c == 3.0);
    return EXIT_SUCCESS;
}

Live example