struct to/from std::tuple conversion

后端 未结 4 1796
[愿得一人]
[愿得一人] 2021-02-05 13:30

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

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


        
4条回答
  •  予麋鹿
    予麋鹿 (楼主)
    2021-02-05 14:21

    Unfortunately there is no automatic way to do that, BUT an alternative is adapt the struct to Boost.Fusion sequence. You do this once and for all for each new class.

    #include 
    ...
    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.

    If you really need to convert to std::tuple, after "Fusion-adapting" you can do this:

    #include 
    #include 
    #include 
    ...
    auto to_tuple(MyStruct const& ms){
       std::tuple 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. It is like having STD containers and no algorithms. Fortunatelly we have #include that allows us to do amazing things.

    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:

    #include 
    #include 
    #include 
    
    #include 
    #include 
    #include 
    
    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
    foo to_foo_aux(std::index_sequence, Tup&& tup) {
      using std::get;
      return {get(std::forward(tup))...};
    }
    template
    foo to_foo(Tup&& tup) {
      using T=std::remove_reference_t;
      return to_foo_aux(
        std::make_index_sequence{}>{},
        std::forward(tup)
      );
    }
    
    template
    auto to_tuple_aux( std::index_sequence, foo const& f ) {
      using boost::fusion::at_c;
      return std::make_tuple(at_c(f)...);
    }
    auto to_tuple(foo const& f){
      using T=std::remove_reference_t;
      return to_tuple_aux(
        std::make_index_sequence::type::value>{},
        f
      );    
    }
    
    int main(){
    
    
        foo f{ "Hello", "World", "!", 1, 2, 3 };
    
        std::tuple 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&>(mystructinstance.i) because that will result in negative votes and it is not portable.

提交回复
热议问题