I was toying around with tuples. I wanted to zip an arbitrary number of tuples. There is probably a better way to do that than what I came up with, but my solution led me to a problem that is interesting in itself.
Sometimes, you want to expand one parameter pack at a time, and those parameter packs can be template arguments and function arguments in the same call. I could not find an obvious way to expand Is
without expanding ts
, besides stuffing ts
into a std::tuple
and unpacking it again using std::get<I>
.
Obviously, it's preferable not to split one function into five functions. I know I could use lambdas, but I'm not sure that would be any cleaner.
Is there a nice way to defer expanding ts
?
https://godbolt.org/z/sY5xMTa7P
#include <iostream>
#include <tuple>
#include <string_view>
template <typename T, typename... Ts>
auto get_first(T t, Ts...) {
return t;
}
template <size_t I, typename... Ts>
auto zip2_impl4(Ts... ts) {
return std::make_tuple(std::get<I>(ts)...);
}
template <size_t I, typename T, size_t... Is>
auto zip2_impl3(T t, std::index_sequence<Is...>) {
return zip2_impl4<I>(std::get<Is>(t)...);
}
template <size_t I, typename T>
auto zip2_impl2(T t) {
using size = std::tuple_size<T>;
using seq = std::make_index_sequence<size::value>;
return zip2_impl3<I>(t, seq{});
}
template <size_t... Is, typename... Ts>
auto zip2_impl(std::index_sequence<Is...> seq, Ts... ts) {
// need to defer expanding the pack ts,
// because the packs Is and ts need to expand separately
auto t = std::make_tuple(ts...);
return std::make_tuple(zip2_impl2<Is>(t)...);
}
template <typename... Ts>
auto zip2(Ts... ts) {
using size = std::tuple_size<decltype(get_first(ts...))>;
using seq = std::make_index_sequence<size::value>;
return zip2_impl(seq{}, ts...);
}
int main() {
using namespace std::literals;
auto ints = std::make_tuple(1,2,3);
auto svs1 = std::make_tuple("a"sv, "b"sv, "c"sv);
auto svs2 = std::make_tuple("d"sv, "e"sv, "f"sv);
auto zipped = zip2(ints, svs1, svs2);
std::apply([](auto... args) {
(std::apply([](auto... args) {
((std::cout << args), ...);
}, args), ...);
}, zipped);
return 0;
}
output: 1ad2be3cf