[英]struct to/from std::tuple conversion
Assuming I have struct
and std::tuple
with same type layout: 假设我有相同类型布局的struct
和std::tuple
:
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? 是否有任何标准化的方式来相互投射?
PS I know that trivial memory copying can do the trick, but it is alignment and implementation dependent PS我知道琐碎的内存复制可以解决这个问题,但它依赖于对齐和实现
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. 现在,为要支持的每个to_tuple_t
一个to_tuple_t
。 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. 我知道无法计算N
所需的值。 So you'd have to type the 3
in auto t = to_tuple<3>(my_struct);
这样你就必须键入3
在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: 可能有一个&&
或&
或decltype可以在这些行上完美转发:
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
. 基于tuple_size
SFINAE支持可能对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>
). 上面的代码适用于所有元组,如std::pair
, std::array
,以及任何自定义代码以支持结构化绑定( tuple_size
和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. 工作并返回一个包含3个元素的元组,因为to_tuple
基于结构化绑定,它与元组to_tuple
作为输入。
to_array
is another possibility in this family. to_array
是这个家庭的另一种可能性。
Unfortunately there is no automatic way to do that, BUT an alternative is adapt the struct to Boost.Fusion sequence. 不幸的是,没有自动的方法可以做到这一点,但另一种方法是将结构调整为Boost.Fusion序列。 You do this once and for all for each new class. 您可以为每个新课程一劳永逸地完成这项工作。
#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)
)
The use MyStruct
as if it where a Fusion.Sequence (it fits generically almost everywhere you already use std::tuple<...>
, if you make those functions generic.) As a bonus you will not need to copy your data members at all. 使用MyStruct
就好像它在Fusion.Sequence的位置(它几乎适用于你已经使用std::tuple<...>
,如果你使这些功能通用。)作为奖励你不需要复制你的数据成员一点都不
If you really need to convert to std::tuple
, after "Fusion-adapting" you can do this: 如果你真的需要转换为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;
}
The truth is that std::tuple
is a half-backed feature. 事实上, std::tuple
是一个半支持的功能。 It is like having STD containers and no algorithms. 这就像拥有STD容器而没有算法。 Fortunatelly we have #include <boost/fusion/adapted/std_tuple.hpp>
that allows us to do amazing things. 幸运的是,我们有#include <boost/fusion/adapted/std_tuple.hpp>
,它可以让我们做出惊人的事情。
Full code: 完整代码:
By including the std_tuple.hpp
header from Boost.Fusion std::tuple
is automatically adapted to a Boost.Fusion sequence, thus the following is possible by using Boost.Fusion as a bridge between your struct and std::tuple
: 通过包含来自Boost.Fusion的std_tuple.hpp
头, std::tuple
会自动适应Boost.Fusion序列,因此可以通过使用Boost.Fusion作为struct和std::tuple
之间的桥梁来实现以下功能:
#include <iostream>
#include <string>
#include <tuple>
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <boost/fusion/algorithm/auxiliary/copy.hpp>
#include <boost/fusion/adapted/std_tuple.hpp>
struct foo
{
std::string a, b, c;
int d, e, f;
};
BOOST_FUSION_ADAPT_STRUCT(
foo,
(std::string, a)
(std::string, b)
(std::string, c)
(int, d)
(int, e)
(int, f)
)
template<std::size_t...Is, class Tup>
foo to_foo_aux(std::index_sequence<Is...>, Tup&& tup) {
using std::get;
return {get<Is>(std::forward<Tup>(tup))...};
}
template<class Tup>
foo to_foo(Tup&& tup) {
using T=std::remove_reference_t<Tup>;
return to_foo_aux(
std::make_index_sequence<std::tuple_size<T>{}>{},
std::forward<Tup>(tup)
);
}
template<std::size_t...Is>
auto to_tuple_aux( std::index_sequence<Is...>, foo const& f ) {
using boost::fusion::at_c;
return std::make_tuple(at_c<Is>(f)...);
}
auto to_tuple(foo const& f){
using T=std::remove_reference_t<foo>;
return to_tuple_aux(
std::make_index_sequence<boost::fusion::result_of::size<foo>::type::value>{},
f
);
}
int main(){
foo f{ "Hello", "World", "!", 1, 2, 3 };
std::tuple<std::string, std::string, std::string, int, int, int> dest = to_tuple(f);
// boost::fusion::copy(f, dest); // also valid but less general than constructor
std::cout << std::get<0>(dest) << ' ' << std::get<1>(dest) << std::get<2>(dest) << std::endl;
std::cout << at_c<0>(dest) << ' ' << at_c<1>(dest) << at_c<2>(dest) << std::endl; // same as above
foo f2 = to_foo(dest);
std::cout << at_c<0>(f2) << ' ' << at_c<1>(f2) << at_c<2>(f2) << std::endl;
}
I will not recommend reinterpret_cast<std::tuple<...>&>(mystructinstance.i)
because that will result in negative votes and it is not portable. 我不建议使用reinterpret_cast<std::tuple<...>&>(mystructinstance.i)
因为这将导致负面投票并且不可移植。
Is there any standartized way to cast one to another? 是否有任何标准化的方式来相互投射?
There is no way to "cast" the one to the other. 没有办法将这一个“施放”到另一个。
The easiest may be to use a std::tie
to pack the tuple out into the struct
; 最简单的方法是使用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;
You can further wrap this up in higher level macros or "generator" ( make
style) functions, eg; 您可以进一步将其包含在更高级别的宏或“生成器”( 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;
}
I know that trivial memory copying can do the trick, but it is alignment and implementation dependent? 我知道琐碎的内存复制可以做到这一点,但它是对齐和实现依赖?
You mention the "trivial memory copy" would work - only for copying the individual members. 你提到“琐碎的记忆副本”会起作用 - 仅用于复制个别成员。 So basically, a memcpy
of the entire structure to the tuple
and vice-versa is not going to always behave as you expect (if ever); 所以基本上,整个结构到tuple
的memcpy
反之亦然,并不总是像你期望的那样(如果有的话); the memory layout of a tuple
is not standardised. tuple
的内存布局不是标准化的。 If it does work, it is highly dependent on the implementation. 如果确实有效,则高度依赖于实施。
Tuple to struct
conversion is trivial, but backward I think is impossible at current C++ level in general. 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 . 实例 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.