Reputation: 2040
In what way can I express that each parameter in a parameter pack to a variadic template is itself a parameter pack?
Consider this code:
template <typename... TS>
void use_tuple(std::tuple<TS...> arg0);
template <typename... TS0, typename... TS1>
void use_tuple(std::tuple<TS0...> arg0, std::tuple<TS1...> arg1);
I want use_tuple
to be able to take any number of tuples. Right now I have to write it like this:
template <typename... TS0, typename... REST>
void use_tuple(std::tuple<TS0...> arg0, REST... rest);
void use_tuple(); // Terminates the recursion.
But I want to write it like this:
// Each ELEMENT in PACK_OF_PACKS is a parameter pack.
template <(typename...)... PACK_OF_PACKS>
void use_tuple(std::tuple<PACK_OF_PACKS...>... args);
Is this even possible? If so, how? If not, what else can I do? My goal for this code is to get at the types contained in all the tuples.
My ultimate goal is something like this:
template <typename...> void foo();
use_tuple(std::tuple<int, float, char>{},
std::tuple<double, short, std::string>{},
std::tuple<std::function<void()>, std::vector<int>>{});
// Results in a call to
// foo<int, float, char, double, short, std::string,
// std::function<void()>, std::vector<int>>();
But I want to implement this in a small, constant number of indirections that does not depend on the number of tuples passed or the number of elements in each. So no recursion.
Upvotes: 4
Views: 2365
Reputation: 1861
I guess you have something like the following (C++17) in mind. Let me stress that I do not recommend it. One should clearly prefer std::tuple_cat
.
http://coliru.stacked-crooked.com/a/c64f2cb9a79af3e2
#include <array>
#include <iostream>
#include <tuple>
#include <utility>
////////////////////////////////////////////////////////////////////////////////
template<class... Ts>
void foo() {
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
////////////////////////////////////////////////////////////////////////////////
namespace detail {
struct Index {
std::size_t outer{};// identify the tuple
std::size_t inner{};// identify the element in the tuple
};
template<std::size_t... ns, class... Tuples>
constexpr void use_tuple(std::index_sequence<ns...>, Tuples...) {
constexpr auto inds = [&] () {
std::array<Index, sizeof...(ns)> inds{};
std::size_t n = 0;
for(std::size_t outer=0; outer<sizeof...(Tuples); ++outer) {
std::size_t size = std::begin({std::tuple_size<Tuples>::value...})[outer];
for(std::size_t inner=0; inner<size; ++inner) inds[n++] = {outer, inner};
}
return inds;
}();
using TupleTuple = std::tuple<Tuples...>;
foo<
std::tuple_element_t<
inds[ns].inner,
std::tuple_element_t<inds[ns].outer, TupleTuple>
>...
>();
}
}// detail
template<class... Tuples>
constexpr void use_tuple(Tuples... tuples) {
constexpr std::size_t N = (std::tuple_size<Tuples>{} + ...);
detail::use_tuple(std::make_index_sequence<N>{}, std::move(tuples)...);
}
////////////////////////////////////////////////////////////////////////////////
int main() {
std::tuple<int, unsigned, int> t0;
std::tuple<double, float> t1;
std::tuple<char, bool, bool, int> t2;
use_tuple(t0, t1, t2);
return 0;
}
Upvotes: 0
Reputation: 50540
My goal for this code is to get at the types contained in all the tuples.
Not sure if you want all of them as an unique pack or not. PACK_OF_PACKS
suggests it actually. In this case, you can just use std::tuple_cat
.
Something along this line should work:
template<typename... T>
void do_use_tuple(std::tuple<T...>) {
// T are the types you are looking for
}
template <typename... T>
void use_tuple(T&&... tuple) {
return do_use_tuple(std::tuple_cat(std::forward<T>(tuple)...));
}
If you are not interested in forwarding also the values, just use decltype
and you can easily get it.
See here for more details.
If you don't want all the parameters packed together and you want to explore them one tuple at a time, you can do something like this instead:
template<typename T>
struct tag { using type = T; };
template<typename... T, typename F>
void use_one(std::tuple<T...> &&, F f) {
F(tag<T>{}...);
}
template <typename... T>
void use_tuple(T&&... tuple) {
int arr = { 0,
(use_one(std::forward<T>(tuple), [](auto... tag) {
// tag::type contains the types of each
// parameter of a tuple, a tuple at a time
}), 0)...
};
(void)arr;
}
It's easy to adjust the lambda and the other function involved if you want also to access the values.
Upvotes: 1
Reputation: 93264
If not, what else can I do?
Why not just pass the tuples themselves as Tuples...
, and then extract the types in the body of use_tuple
?
template <typename...>
using example = int;
template <typename... Ts>
void foo(Ts...) { }
template <typename... Tuples>
void use_tuple(Tuples...)
{
foo(typename types<Tuples>::template apply<example>{}...);
}
You can extract the types with an helper types
class:
template <typename Tuple>
struct types;
template <typename... Ts>
struct types<std::tuple<Ts...>>
{
template <template <typename...> class T>
using apply = T<Ts...>;
};
Upvotes: 1