Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

struct to/from std::tuple conversion

Tags:

c++

c++17

Assuming I have struct and std::tuple with same type layout:

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

Is there any standartized way to cast one to another?

P.S. I know that trivial memory copying can do the trick, but it is alignment and implementation dependent

like image 525
Andrei R. Avatar asked Jul 25 '16 06:07

Andrei R.


People also ask

When would you use a tuple instead of a struct?

You use a struct for things that meaningfully belong together to form a whole. You use a tuple for things that are together coincidentally. You can use a tuple spontaneously in your code.

Is a struct a tuple?

Structs are similar to tuples, discussed in “The Tuple Type” section, in that both hold multiple related values. Like tuples, the pieces of a struct can be different types.

Is std :: tuple a container?

The new std::array and std::tuple containers provide developers with additional ways to manage structured data efficiently.

What is std :: tuple?

Class template std::tuple is a fixed-size collection of heterogeneous values. It is a generalization of std::pair.


1 Answers

We can use structured bindings to convert a struct into a tuple with a bit of work.

Struct-to-tuple is very awkward.

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

Now, write a to_tuple_t for each size you want to support. This gets tedious. Sadly I know of no way to introduce a parameter pack there.

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

I know of no way to calculate the value of N required either. So you'd have to type the 3 in auto t = to_tuple<3>(my_struct); when you call it.

I am not a master of structured bindings. There is probably a && or & or a decltype that would permit perfect forwarding on these lines:

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

but without a compiler to play with, I'll be conservative and make redundant copies.


Converting a tuple into a struct is easy:

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

SFINAE support based off tuple_size might be good for to_struct.

The above code works with all tuple-likes, like std::pair, std::array, and anything you custom-code to support structured bindings (tuple_size and get<I>).


Amusingly,

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

works and returns a tuple with 3 elements, as to_tuple is based on structured bindings, which work with tuple-likes as input.

to_array is another possibility in this family.

like image 114
Yakk - Adam Nevraumont Avatar answered Nov 02 '22 11:11

Yakk - Adam Nevraumont