Creating a sub-tuple starting with std :: tuple <some_types ...>
Suppose a std::tuple<some_types...>
. I would like to create a new std::tuple
whose types are indexed in [0, sizeof...(some_types) - 2]
. Suppose, for example, that the original tuple is std::tuple<int, double, bool>
. I would like to get a subcategory defined as std::tuple<int, double>
.
I am new to variable templates. As a first step, I tried to write a struct
to store different types of the original std::tuple
in order to create a new tuple of the same type (as in std::tuple<decltype(old_tuple)> new_tuple
).
template<typename... types> struct type_list; template<typename T, typename... types> struct type_list<T, types...> : public type_list<types...> { typedef T type; }; template<typename T> struct type_list<T> { typedef T type; };
What I would like to do is something like:
std::tuple<type_list<bool, double, int>::type...> new_tuple // this won't work
And the next step is to discard the last element in the parameter package. How can I access multiple type_list
stored in type_list
? and how to refuse some of them?
Thanks.
Such a manipulation is quite simple using the index sequence method: generate an index sequence with two lower indices than your tuple, and use this sequence to select fields from the original. Using std::make_index_sequence
and the return type of output from C ++ 14:
template <typename... T, std::size_t... I> auto subtuple_(const std::tuple<T...>& t, std::index_sequence<I...>) { return std::make_tuple(std::get<I>(t)...); } template <int Trim, typename... T> auto subtuple(const std::tuple<T...>& t) { return subtuple_(t, std::make_index_sequence<sizeof...(T) - Trim>()); }
In C ++ 11:
#include <cstddef> // for std::size_t template<typename T, T... I> struct integer_sequence { using value_type = T; static constexpr std::size_t size() noexcept { return sizeof...(I); } }; namespace integer_sequence_detail { template <typename, typename> struct concat; template <typename T, T... A, T... B> struct concat<integer_sequence<T, A...>, integer_sequence<T, B...>> { typedef integer_sequence<T, A..., B...> type; }; template <typename T, int First, int Count> struct build_helper { using type = typename concat< typename build_helper<T, First, Count/2>::type, typename build_helper<T, First + Count/2, Count - Count/2>::type >::type; }; template <typename T, int First> struct build_helper<T, First, 1> { using type = integer_sequence<T, T(First)>; }; template <typename T, int First> struct build_helper<T, First, 0> { using type = integer_sequence<T>; }; template <typename T, T N> using builder = typename build_helper<T, 0, N>::type; } // namespace integer_sequence_detail template <typename T, T N> using make_integer_sequence = integer_sequence_detail::builder<T, N>; template <std::size_t... I> using index_sequence = integer_sequence<std::size_t, I...>; template<size_t N> using make_index_sequence = make_integer_sequence<size_t, N>; #include <tuple> template <typename... T, std::size_t... I> auto subtuple_(const std::tuple<T...>& t, index_sequence<I...>) -> decltype(std::make_tuple(std::get<I>(t)...)) { return std::make_tuple(std::get<I>(t)...); } template <int Trim, typename... T> auto subtuple(const std::tuple<T...>& t) -> decltype(subtuple_(t, make_index_sequence<sizeof...(T) - Trim>())) { return subtuple_(t, make_index_sequence<sizeof...(T) - Trim>()); }
Here you can solve the problem directly.
template<unsigned...s> struct seq { typedef seq<s...> type; }; template<unsigned max, unsigned... s> struct make_seq:make_seq<max-1, max-1, s...> {}; template<unsigned...s> struct make_seq<0, s...>:seq<s...> {}; template<unsigned... s, typename Tuple> auto extract_tuple( seq<s...>, Tuple& tup ) { return std::make_tuple( std::get<s>(tup)... ); }
You can use it as follows:
std::tuple< int, double, bool > my_tup; auto short_tup = extract_tuple( make_seq<2>(), my_tup ); auto skip_2nd = extract_tuple( seq<0,2>(), my_tup );
and use decltype
if you need the resulting type.
A completely different approach would be to write append_type
, which takes the type and tuple<...>
, and adds that type to the end. Then add to type_list
:
template<template<typename...>class target> struct gather { typedef typename type_list<types...>::template gather<target>::type parent_result; typedef typename append< parent_result, T >::type type; };
which gives you a way to copy the types of your type_list
into an arbitrary parameter package containing template
. But this is not required for your problem.
One way to do this is to recursively pass two tuples to a helper structure, which takes the first element of the root "source" and adds it to the end of the other:
#include <iostream> #include <tuple> #include <type_traits> namespace detail { template<typename...> struct truncate; // this specialization does the majority of the work template<typename... Head, typename T, typename... Tail> struct truncate< std::tuple<Head...>, std::tuple<T, Tail...> > { typedef typename truncate< std::tuple<Head..., T>, std::tuple<Tail...> >::type type; }; // this one stops the recursion when there only // one element left in the source tuple template<typename... Head, typename T> struct truncate< std::tuple<Head...>, std::tuple<T> > { typedef std::tuple<Head...> type; }; } template<typename...> struct tuple_truncate; template<typename... Args> struct tuple_truncate<std::tuple<Args...>> { // initiate the recursion - we start with an empty tuple, // with the source tuple on the right typedef typename detail::truncate< std::tuple<>, std::tuple<Args...> >::type type; }; int main() { typedef typename tuple_truncate< std::tuple<bool, double, int> >::type X; // test std::cout << std::is_same<X, std::tuple<bool, double>>::value; // 1, yay }
A subrange from a tuple with bounds checking, without declaring "helper classes":
template <size_t starting, size_t elems, class tuple, class seq = decltype(std::make_index_sequence<elems>())> struct sub_range; template <size_t starting, size_t elems, class ... args, size_t ... indx> struct sub_range<starting, elems, std::tuple<args...>, std::index_sequence<indx...>> { static_assert(elems <= sizeof...(args) - starting, "sub range is out of bounds!"); using tuple = std::tuple<std::tuple_element_t<indx + starting, std::tuple<args...>> ...>; };
Using:
struct a0; ... struct a8; using range_outer = std::tuple<a0, a1, a2, a3, a4, a5, a6, a7, a8>; sub_range<2, 3, range_outer>::tuple; //std::tuple<a2, a3, a4>