I have a tuple of unknown size (it\'s template parametr of method)
Is it way to get part of it (I need throw away first element of it)
For example, I have
With help of a compile-time integer list:
#include <cstdlib>
template <size_t... n>
struct ct_integers_list {
template <size_t m>
struct push_back
{
typedef ct_integers_list<n..., m> type;
};
};
template <size_t max>
struct ct_iota_1
{
typedef typename ct_iota_1<max-1>::type::template push_back<max>::type type;
};
template <>
struct ct_iota_1<0>
{
typedef ct_integers_list<> type;
};
We could construct the tail simply by parameter-pack expansion:
#include <tuple>
template <size_t... indices, typename Tuple>
auto tuple_subset(const Tuple& tpl, ct_integers_list<indices...>)
-> decltype(std::make_tuple(std::get<indices>(tpl)...))
{
return std::make_tuple(std::get<indices>(tpl)...);
// this means:
// make_tuple(get<indices[0]>(tpl), get<indices[1]>(tpl), ...)
}
template <typename Head, typename... Tail>
std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& tpl)
{
return tuple_subset(tpl, typename ct_iota_1<sizeof...(Tail)>::type());
// this means:
// tuple_subset<1, 2, 3, ..., sizeof...(Tail)-1>(tpl, ..)
}
Usage:
#include <cstdio>
int main()
{
auto a = std::make_tuple(1, "hello", 7.9);
auto b = tuple_tail(a);
const char* s = nullptr;
double d = 0.0;
std::tie(s, d) = b;
printf("%s %g\n", s, d);
// prints: hello 7.9
return 0;
}
(On ideone: http://ideone.com/Tzv7v; the code works in g++ 4.5 to 4.7 and clang++ 3.0)
The c++17 way:
#include <tuple>
#include <string>
template <size_t __begin, size_t...__indices, typename Tuple>
auto tuple_slice_impl(Tuple &&tuple, std::index_sequence<__indices...>) {
return std::make_tuple(std::get<__begin + __indices>(std::forward<Tuple>(tuple))...);
}
template <size_t __begin, size_t __count, typename Tuple>
auto tuple_slice(Tuple &&tuple) {
static_assert(__count > 0, "splicing tuple to 0-length is weird...");
return tuple_slice_impl<__begin>(std::forward<Tuple>(tuple), std::make_index_sequence<__count>());
}
template <size_t __begin, size_t __count, typename Tuple>
using tuple_slice_t = decltype(tuple_slice<__begin, __count>(Tuple{}));
using test_tuple = std::tuple<int, int, bool, nullptr_t, std::string>;
using sliced_test = tuple_slice_t<2, 2, test_tuple>;
static_assert(std::tuple_size_v<sliced_test> == 2);
static_assert(std::is_same_v<std::decay_t<decltype(std::get<0>(sliced_test{}))>, bool>);
static_assert(std::is_same_v<std::decay_t<decltype(std::get<1>(sliced_test{}))>, nullptr_t>);
#include <cassert>
int main() {
test_tuple tuple {
-1, 42, true, nullptr, "hello"
};
auto spliced = tuple_slice<3, 2>(tuple);
assert(std::get<0>(spliced) == nullptr);
assert(std::get<1>(spliced) == std::get<4>(tuple));
return 0;
}
A tuple slice operation (that also works for std::array
and std::pair
) can be defined like this (C++14 required):
namespace detail
{
template <std::size_t Ofst, class Tuple, std::size_t... I>
constexpr auto slice_impl(Tuple&& t, std::index_sequence<I...>)
{
return std::forward_as_tuple(
std::get<I + Ofst>(std::forward<Tuple>(t))...);
}
}
template <std::size_t I1, std::size_t I2, class Cont>
constexpr auto tuple_slice(Cont&& t)
{
static_assert(I2 >= I1, "invalid slice");
static_assert(std::tuple_size<std::decay_t<Cont>>::value >= I2,
"slice index out of bounds");
return detail::slice_impl<I1>(std::forward<Cont>(t),
std::make_index_sequence<I2 - I1>{});
}
And an arbitrary subset of a tuple t
can be obtained like so :
tuple_slice<I1, I2>(t);
Where [I1, I2)
is the exclusive range of the subset and the return value is a tuple of either references or values depending on whether the input tuple is an lvalue or an rvalue respectively (a thorough elaboration can be found in my blog).
Please don't use!
int
but may fail for your type!).See comments for discussion. I'm leaving this answer just for reference.
Even simpler:
tuple<int,int,int> origin{7,12,42};
tuple<int, int> &tail1 = (tuple<int, int>&)origin;
tuple<int> &tail2 = (tuple<int>&)origin;
cout << "tail1: {" << get<0>(tail1) << ", " << get<1>(tail1) << "}" << endl;
cout << "tail2: {" << get<0>(tail2) << "}" << endl;
I got:
tail1: {12, 42}
tail2: {42}
I'm not certain that this is not an unspecified behaviour. Works for me: Fedora 20 and
❯ clang --version
clang version 3.3 (tags/RELEASE_33/final)
Target: x86_64-redhat-linux-gnu
Thread model: posix
❯ gcc --version
gcc (GCC) 4.8.2 20131212 (Red Hat 4.8.2-7)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
References: article on voidnish.wordpress.com/.
With C++17, you can use std::apply:
template <typename Head, typename... Tail>
std::tuple<Tail...> tuple_tail(const std::tuple<Head, Tail...>& t)
{
return apply([](auto head, auto... tail) {
return std::make_tuple(tail...)};
}, t);
}
I made some modifications to Adam's code that would strip off the first N arguments of the tuple, as well as create a new tuple with only the last N types ... Here is the complete code (note: if anyone decides to +1 my answer, also please +1 Adam's answer since that is what this code is based on, and I don't wish to take any credit away from his contribution):
//create a struct that allows us to create a new tupe-type with the first
//N types truncated from the front
template<size_t N, typename Tuple_Type>
struct tuple_trunc {};
template<size_t N, typename Head, typename... Tail>
struct tuple_trunc<N, std::tuple<Head, Tail...>>
{
typedef typename tuple_trunc<N-1, std::tuple<Tail...>>::type type;
};
template<typename Head, typename... Tail>
struct tuple_trunc<0, std::tuple<Head, Tail...>>
{
typedef std::tuple<Head, Tail...> type;
};
/*-------Begin Adam's Code-----------
Note the code has been slightly modified ... I didn't see the need for the extra
variadic templates in the "assign" structure. Hopefully this doesn't break something
I didn't forsee
*/
template<size_t N, size_t I>
struct assign
{
template<class ResultTuple, class SrcTuple>
static void x(ResultTuple& t, const SrcTuple& tup)
{
std::get<I - N>(t) = std::get<I>(tup);
assign<N, I - 1>::x(t, tup); //this offsets the assignment index by N
}
};
template<size_t N>
struct assign<N, 1>
{
template<class ResultTuple, class SrcTuple>
static void x(ResultTuple& t, const SrcTuple& tup)
{
std::get<0>(t) = std::get<1>(tup);
}
};
template<size_t TruncSize, class Tup> struct th2;
//modifications to this class change "type" to the new truncated tuple type
//as well as modifying the template arguments to assign
template<size_t TruncSize, class Head, class... Tail>
struct th2<TruncSize, std::tuple<Head, Tail...>>
{
typedef typename tuple_trunc<TruncSize, std::tuple<Head, Tail...>>::type type;
static type tail(const std::tuple<Head, Tail...>& tup)
{
type t;
assign<TruncSize, std::tuple_size<type>::value>::x(t, tup);
return t;
}
};
template<size_t TruncSize, class Tup>
typename th2<TruncSize, Tup>::type tail(const Tup& tup)
{
return th2<TruncSize, Tup>::tail(tup);
}
//a small example
int main()
{
std::tuple<double, double, int, double> test(1, 2, 3, 4);
tuple_trunc<2, std::tuple<double, double, int, double>>::type c = tail<2>(test);
return 0;
}