0

I want to group data of tuples in tuples into a tuple of vectors. Given is a tuple of tuples containing data. There are multiple duplicate types, that's data should be grouped into a vector of each unique type.

So far boost::mp11 is the most elegant way I found, to build a type std::tuple<std::vector<T1>, std::tuple<std::vector<T2>, ...> based on an incoming parameter pack std::tuple<std::tuple<some_data>, std::tuple<std::tuple<some_more_data>, ... using mp_unique and mp_transform to get vectors for each unique type.

Happy if you know a std::ish alternative (optional).

I am stuck finding a way to fill tuple elements into matching vectors? I'd be excited, finding a fold expression'ish way to do so.

This example should help to give a better idea of what I have in mind.

template <typename T> using vector_of = std::vector<T>;

static constexpr auto tuple_to_vector(auto&&... Vs) noexcept {
    // merged_tuple = std::tuple<int, double, int, char, int, float, char, float, double>
    auto merged_tuple = std::tuple_cat(std::forward<decltype(Vs)>(Vs)...);
  
    // vector_t = std::tuple<std::vector<int>, std::vector<double>, std::vector<char>, std::vector<float>>
    using vector_t = boost::mp11::mp_transform<vector_of, boost::mp11::mp_unique<decltype(merged_tuple)>>; 
    vector_t vec;

    // how to add merged_tuple elements to vec
    // resulting in
    // std::tuple< std::vector<int>{1,3,2}, std::vector<double>{2.0,3.0}, std::vector<char>{'b','c'}, std::vector<float>{3.0f,2.0f}>
  
    return std::move(vec);
    
};

int main() {
    constexpr auto vec = tuple_to_vector(
        std::make_tuple(1,2.0,3),
        std::make_tuple('b',2,3.0f),
        std::make_tuple('c',2.0f,3.0)
    );
    // expecting
    // vec = std::tuple<
    //  std::vector<int>{1,3,2},
    //  std::vector<double>{2.0,3.0},
    //  std::vector<char>{'b','c'},
    //  std::vector<float>{3.0f,2.0f}
    // >
    return 42;
}
Chris G.
  • 816
  • 2
  • 15

1 Answers1

1

You can use std::apply to expand the elements of merged_tuple, and use std::get to extract the corresponding vector in vector_t according to the type of the element, and fill into the vector through push_back

std::apply([&vec](auto... args) {
   (std::get<std::vector<decltype(args)>>(vec).push_back(args), ...);
}, merged_tuple);

Demo

Note that vec cannot be constexpr even in C++20 since its allocation is non-transient. If you really want to construct a constexpr tuple, then you can use std::array since the size of the array can be obtained at compile time. Here is a demo that converts the resulting vec to the corresponding std::array type.

康桓瑋
  • 33,481
  • 5
  • 40
  • 90
  • Perfect 康桓瑋! Your proposal does exactly, what I was looking for. ```constexpr``` seems indeed not yet to be possible in C++20, due to lacking allocation capabilities of ```std::vector```. Thanks for pointing that out. I try to find a workaround for that specific detail. – Chris G. Apr 30 '22 at 19:43
  • 1
    "*I try to find a workaround for that specific detail.*" `std::array` is definitely an option, see updated answer. – 康桓瑋 May 01 '22 at 03:16
  • Thanks for sharing that neat approach! Do you see a way to directly initialize ```array_t arr = ...```? In case of having instances of classes without standard constructor it's a bit tricky to initialize. – Chris G. May 01 '22 at 16:59