Related
I would like my class
template <class T, unsigned int n>
class X;
to create a std::tuple that contains n times the type T. Is there a particularly neat way for this? Is there also a nice way to do this for arbitrary variadic template classes?
This is what I did first:
#include <tuple>
template <class, unsigned int, class>
struct simple_repeat_helper;
template <class T, unsigned int n, class... Args>
struct simple_repeat_helper<T, n, std::tuple<Args...>>
{
typedef typename simple_repeat_helper<T, n-1, std::tuple<Args..., T>>::type type;
};
template <class T, class... Args>
struct simple_repeat_helper<T, 0, std::tuple<Args...>>
{
typedef std::tuple<Args...> type;
};
template <class T, unsigned int n>
struct simple_repeat
{
using type = typename simple_repeat_helper<T, n, std::tuple<>>::type;
};
But actually, I do not need this for std::tuple, but for another class that acts similarly. So I thought that I would create a version that is a little bit more generic:
template <class, unsigned int, template <class...> class, class>
struct repeat_helper;
template <class T, template <class...> class M, class... Args>
struct repeat_helper<T, 0, M, M<Args...>>
{
typedef M<Args...> type;
};
template <class T, unsigned int n, template <class...> class M, class... Args>
struct repeat_helper<T, n, M, M<Args...>>
{
typedef typename repeat_helper<T, n-1, M, M<Args..., T>>::type type;
};
template <class T, unsigned int n, template <class...> class M = std::tuple>
struct repeat
{
using type = typename repeat_helper<T, n, M, M<>>::type;
};
I thought that I could use it like this:
repeat<double, 5, std::tuple>::type x = std::make_tuple( 1., 2., 3., 4., 5. );
But unfortunately it fails to compile due to:
ambiguous class template instantiation for ‘struct repeat_helper<double, 0u, std::tuple, std::tuple<double, double, double, double, double> >’
Any help on this error would be appreciated!
I would do it this way:
template<typename, typename>
struct append_to_type_seq { };
template<typename T, typename... Ts, template<typename...> class TT>
struct append_to_type_seq<T, TT<Ts...>>
{
using type = TT<Ts..., T>;
};
template<typename T, unsigned int N, template<typename...> class TT>
struct repeat
{
using type = typename
append_to_type_seq<
T,
typename repeat<T, N-1, TT>::type
>::type;
};
template<typename T, template<typename...> class TT>
struct repeat<T, 0, TT>
{
using type = TT<>;
};
As a small test:
#include <type_traits>
#include <tuple>
template<typename... Ts>
struct X { };
int main()
{
repeat<double, 5, std::tuple>::type t = std::make_tuple(1., 2., 3., 4., 5.);
static_assert(
std::is_same<
decltype(t),
std::tuple<double, double, double, double, double>
>::value, "!");
repeat<double, 3, X>::type y;
static_assert(
std::is_same<decltype(y), X<double, double, double>>::value, "!");
}
Finally, a live example.
An indices-based solution:
template<typename Dependent, int Index>
using DependOn = Dependent;
// Assuming e.g. Indices<3> is indices<0, 1, 2>
template<typename T, int N, typename I = Indices<N>>
struct repeat;
template<typename T, int N, int... Indices>
struct repeat<T, N, indices<Indices...>> {
// Can be an actual type-list instead of (ab)using std::tuple
using type = std::tuple<DependOn<T, Indices>...>;
};
C++14 flavour:
template<typename Dependent, std::size_t Index>
using DependOn = Dependent;
template<typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
struct repeat;
template<typename T, std::size_t N, std::size_t... Indices>
struct repeat<T, N, std::index_sequence<Indices...>> {
using type = std::tuple<DependOn<T, Indices>...>;
};
Let us suppose that a std::tuple<some_types...> is given. I would like to create a new std::tuple whose types are the ones indexed in [0, sizeof...(some_types) - 2]. For instance, let's suppose that the starting tuple is std::tuple<int, double, bool>. I would like to obtain a sub-tuple defined as std::tuple<int, double>.
I'm quite new to variadic templates. As a first step I tried to write a struct in charge of storing the different types of the original std::tuple with the aim of creating a new tuple of the same kind (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 would be of discarding the last element in the parameter pack. How can I access the several type's stored in type_list? and how to discard some of them?
Thanks.
Here is a way to solve your 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 this 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 other approach would be to write append_type, which takes a type and a 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 accumulate the types of your type_list into an arbitrary parameter pack holding template. But that isn't required for your problem.
This kind of manipulation is fairly easy with an index sequence technique: generate an index sequence with two fewer indices than your tuple, and use that sequence to select fields from the original. Using std::make_index_sequence and return type deduction 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>());
}
Live at Coliru.
Subrange from tuple with boundary 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...>> ...>;
};
Usage:
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>
One way to do it is to recursively pass two tuples to a helper struct that takes the first element of the "source" tuple and adds it to the end of the another one:
#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's 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
}
Live example.
Consider the following code:
template <class Scalar, class Array, class Tuple>
class Test {};
where Array is a std::array, and Tuple is a std::tuple. In this class, I will have a lot of SFINAE, and I would like to create a big tuple called Types that will contain the complete list of types. This will allow me to test some conditions with variadic lists.
So the challenge is to create a type that will have the following behaviour. If:
Scalar = int
Array = std::array<double, 3>
Tuple = std::tuple<char, float, std::string>
Then:
Types = std::tuple<int, double, double, double, char, float, std::string>
which is the concatenation of the internal data of the Scalar, Array and Tuple.
How to do that ?
This seems to work:
template<typename T1, typename T2>
struct concat_tuples;
template<typename... T1, typename... T2>
struct concat_tuples<std::tuple<T1...>, std::tuple<T2...>>
{
using type = std::tuple<T1..., T2...>;
};
// n_tuple<int, 3>::type == std::tuple<int, int, int>
template<typename T, size_t n>
struct n_tuple;
template<typename T>
struct n_tuple<T, 0>
{
using type = std::tuple<>;
};
template<typename T, size_t n>
struct n_tuple
{
using type = typename concat_tuples<
typename n_tuple<T, n-1>::type,
std::tuple<T>
>::type;
};
template <class Scalar, class Array, class Tuple>
struct Test;
template <class Scalar, typename T, size_t n, typename... Ts>
struct Test<Scalar, std::array<T, n>, std::tuple<Ts...>>
{
using type = typename concat_tuples<
typename concat_tuples<
std::tuple<Scalar>,
typename n_tuple<T, n>::type
>::type,
std::tuple<Ts...>
>::type;
};
Live demo here.
Use a partial specialization to deduce the types, and Xeo's tuple_cat comment to put them together (Live at Coliru:
template <typename ArrayType, std::size_t ArraySize, typename... Fields>
struct ArrayTuple : ArrayTuple<ArrayType, ArraySize-1, ArrayType, Fields...> {};
template <typename ArrayType, typename... Fields>
struct ArrayTuple<ArrayType, 0, Fields...> {
using type = std::tuple<Fields...>;
};
template <typename, typename, typename> class Test;
template <typename Scalar, typename ArrayType, std::size_t ArraySize, typename... Fields>
class Test<Scalar, std::array<ArrayType, ArraySize>, std::tuple<Fields...>> {
public:
// Modified tuple_cat code from Xeo's comment:
using Tuple = std::tuple<Fields...>;
using Types = decltype(
std::tuple_cat(std::tuple<Scalar>(),
typename ArrayTuple<ArrayType, ArraySize>::type(),
std::declval<Tuple>()));
};
This seems to be a very simple question: How does one remove the first (the n-th) type in a std::tuple?
Example:
typedef std::tuple<int, short, double> tuple1;
typedef std::tuple<short, double> tuple2;
The operation described above would transform tuple1 into tuple2. Is it possible?
You can use a simple type function based on partial specialization of a class template:
#include <type_traits>
#include <tuple>
using namespace std;
template<typename T>
struct remove_first_type
{
};
template<typename T, typename... Ts>
struct remove_first_type<tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};
int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_first_type<my_tuple>::type my_tuple_wo_first_type;
static_assert(
is_same<my_tuple_wo_first_type, tuple<bool, double>>::value,
"Error!"
);
}
Also, this solution can be easily generalized to remove the i-th type of a tuple:
#include <type_traits>
#include <tuple>
using namespace std;
template<size_t I, typename T>
struct remove_ith_type
{
};
template<typename T, typename... Ts>
struct remove_ith_type<0, tuple<T, Ts...>>
{
typedef tuple<Ts...> type;
};
template<size_t I, typename T, typename... Ts>
struct remove_ith_type<I, tuple<T, Ts...>>
{
typedef decltype(
tuple_cat(
declval<tuple<T>>(),
declval<typename remove_ith_type<I - 1, tuple<Ts...>>::type>()
)
) type;
};
int main()
{
typedef tuple<int, bool, double> my_tuple;
typedef remove_ith_type<1, my_tuple>::type my_tuple_wo_2nd_type;
static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!"
);
}
I wrote a proposal which was accepted into the C++14 standard making it quite easy to do for any "tuple-like" type, i.e. one that supports the tuple_size and tuple_element API:
template<typename T, typename Seq>
struct tuple_cdr_impl;
template<typename T, std::size_t I0, std::size_t... I>
struct tuple_cdr_impl<T, std::index_sequence<I0, I...>>
{
using type = std::tuple<typename std::tuple_element<I, T>::type...>;
};
template<typename T>
struct tuple_cdr
: tuple_cdr_impl<T, std::make_index_sequence<std::tuple_size<T>::value>>
{ };
And you can transform a tuple object into the new type with only a couple of functions:
template<typename T, std::size_t I0, std::size_t... I>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr_impl(T&& t, std::index_sequence<I0, I...>)
{
return std::make_tuple(std::get<I>(t)...);
}
template<typename T>
typename tuple_cdr<typename std::remove_reference<T>::type>::type
cdr(T&& t)
{
return cdr_impl(std::forward<T>(t),
std::make_index_sequence<std::tuple_size<T>::value>{});
}
This creates an integer sequence [0,1,2,...,N) where N is tuple_size<T>::value, then creates a new tuple with make_tuple(get<I>(t)...) for I in [1,2,...,N)
Testing it:
using tuple1 = std::tuple<int, short, double>;
using tuple2 = std::tuple<short, double>;
using transformed = decltype(cdr(std::declval<tuple1>()));
static_assert(std::is_same<transformed, tuple2>::value, "");
static_assert(std::is_same<tuple_cdr<tuple1>::type, tuple2>::value, "");
#include <iostream>
int main()
{
auto t = cdr(std::make_tuple(nullptr, "hello", "world"));
std::cout << std::get<0>(t) << ", " << std::get<1>(t) << '\n';
}
My reference implementation for the proposal is at https://gitlab.com/redistd/integer_seq/blob/master/integer_seq.h
I came up with a solution very similar to that proposed by #Andy, but that tries to be a bit more generic by working directly on the parameter pack (using a dummy wrapper) rather than on std::tuple. This way, the operation can be applied to other variadic templates as well, not only to tuples:
#include <type_traits>
#include <tuple>
template <typename... Args> struct pack {};
template <template <typename...> class T, typename Pack>
struct unpack;
template <template <typename...> class T, typename... Args>
struct unpack<T, pack<Args...>>
{
typedef T<Args...> type;
};
template <typename T, typename Pack>
struct prepend;
template <typename T, typename... Args>
struct prepend<T, pack<Args...>>
{
typedef pack<T, Args...> type;
};
template <std::size_t N, typename... Args>
struct remove_nth_type;
template <std::size_t N, typename T, typename... Ts>
struct remove_nth_type<N, T, Ts...>
: prepend<T, typename remove_nth_type<N-1, Ts...>::type>
{};
template <typename T, typename... Ts>
struct remove_nth_type<0, T, Ts...>
{
typedef pack<Ts...> type;
};
template <typename T, int N>
struct remove_nth;
template <template <typename...> class T, int N, typename... Args>
struct remove_nth<T<Args...>, N>
{
typedef typename
unpack<
T, typename
remove_nth_type<N, Args...>::type
>::type type;
};
template <typename... Args>
struct my_variadic_template
{
};
int main()
{
typedef std::tuple<int, bool, double> my_tuple;
typedef remove_nth<my_tuple, 1>::type my_tuple_wo_2nd_type;
static_assert(
is_same<my_tuple_wo_2nd_type, tuple<int, double>>::value,
"Error!"
);
typedef my_variadic_template<int, double> vt;
typedef remove_nth<vt, 0>::type vt_wo_1st_type;
static_assert(
is_same<vt_wo_1st_type, my_variadic_template<double>>::value,
"Error!"
);
}
pack is an helper structure whose sole purpose is to store a template parameter pack. unpack can then be used to unpack the parameters into an arbitrary class template (thanks to #BenVoigt for this trick). prepend simply prepends a type to a pack.
remove_nth_type uses partial template specialization to remove the nth type from a parameter pack, storing the result into a pack. Finally, remove_nth takes a specialization of an arbitrary class template, remove the nth type from its template parameters, and return the new specialization.
Beside that crazy TMP stuff, there is a very easy way using the C++17 STL function std::apply:
#include <string>
#include <tuple>
template <class T, class... Args>
auto tail(const std::tuple<T, Args...>& t)
{
return std::apply(
[](const T&, const Args&... args)
{
return std::make_tuple(args...);
}, t);
}
template <class T>
using tail_t = decltype(tail(T{}));
int main()
{
std::tuple<int, double, std::string> t{1, 2., "3"};
auto _2_3 = tail(t);
using tuple_t = tail_t<std::tuple<int, double, std::string>>;
static_assert(std::is_same_v<std::tuple<double, std::string>, tuple_t>);
}
DEMO.
This is an over engineered bit of template metaprogramming for this task. It includes the ability to do arbitrary reorders/duplications/removals on the types of a tuple via a filter template:
#include <utility>
#include <type_traits>
template<typename... Ts> struct pack {};
template<std::size_t index, typename Pack, typename=void> struct nth_type;
template<typename T0, typename... Ts>
struct nth_type<0, pack<T0, Ts...>, void> { typedef T0 type; };
template<std::size_t index, typename T0, typename... Ts>
struct nth_type<index, pack<T0, Ts...>, typename std::enable_if<(index>0)>::type>:
nth_type<index-1, pack<Ts...>>
{};
template<std::size_t... s> struct seq {};
template<std::size_t n, std::size_t... s>
struct make_seq:make_seq<n-1, n-1, s...> {};
template<std::size_t... s>
struct make_seq<0,s...> {
typedef seq<s...> type;
};
template<typename T, typename Pack> struct conc_pack { typedef pack<T> type; };
template<typename T, typename... Ts> struct conc_pack<T, pack<Ts...>> { typedef pack<T, Ts...> type; };
template<std::size_t n, typename Seq> struct append;
template<std::size_t n, std::size_t... s>
struct append<n, seq<s...>> {
typedef seq<n, s...> type;
};
template<typename S0, typename S1> struct conc;
template<std::size_t... s0, std::size_t... s1>
struct conc<seq<s0...>, seq<s1...>>
{
typedef seq<s0..., s1...> type;
};
template<typename T, typename=void> struct value_exists:std::false_type {};
template<typename T> struct value_exists<T,
typename std::enable_if< std::is_same<decltype(T::value),decltype(T::value)>::value >::type
>:std::true_type {};
template<typename T, typename=void> struct result_exists:std::false_type {};
template<typename T> struct result_exists<T,
typename std::enable_if< std::is_same<typename T::result,typename T::result>::value >::type
>:std::true_type {};
template<template<std::size_t>class filter, typename Seq, typename=void>
struct filter_seq { typedef seq<> type; };
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<value_exists<filter<s0>>::value>::type>
: append< filter<s0>::value, typename filter_seq<filter, seq<s...>>::type >
{};
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && result_exists<filter<s0>>::value>::type>
: conc< typename filter<s0>::result, typename filter_seq<filter, seq<s...>>::type >
{};
template<template<std::size_t>class filter, std::size_t s0, std::size_t... s>
struct filter_seq<filter, seq<s0, s...>, typename std::enable_if<!value_exists<filter<s0>>::value && !result_exists<filter<s0>>::value>::type>
: filter_seq<filter, seq<s...>>
{};
template<typename Seq, typename Pack>
struct remap_pack {
typedef pack<> type;
};
template<std::size_t s0, std::size_t... s, typename Pack>
struct remap_pack< seq<s0, s...>, Pack >
{
typedef typename conc_pack< typename nth_type<s0, Pack>::type, typename remap_pack< seq<s...>, Pack >::type >::type type;
};
template<typename Pack>
struct get_indexes { typedef seq<> type; };
template<typename... Ts>
struct get_indexes<pack<Ts...>> {
typedef typename make_seq< sizeof...(Ts) >::type type;
};
template<std::size_t n>
struct filter_zero_out { enum{ value = n }; };
template<>
struct filter_zero_out<0> {};
template<std::size_t n>
struct filter_zero_out_b { typedef seq<n> result; };
template<>
struct filter_zero_out_b<0> { typedef seq<> result; };
#include <iostream>
int main() {
typedef pack< int, double, char > pack1;
typedef pack< double, char > pack2;
typedef filter_seq< filter_zero_out, typename get_indexes<pack1>::type >::type reindex;
typedef filter_seq< filter_zero_out_b, typename get_indexes<pack1>::type >::type reindex_b;
typedef typename remap_pack< reindex, pack1 >::type pack2_clone;
typedef typename remap_pack< reindex_b, pack1 >::type pack2_clone_b;
std::cout << std::is_same< pack2, pack2_clone >::value << "\n";
std::cout << std::is_same< pack2, pack2_clone_b >::value << "\n";
}
Here we have a type pack that holds an arbitrary list of types. See #LucTouraille 's neat answer for how to move between tuple and pack.
seq holds a sequence of indexes. remap_pack takes a seq and a pack, and builds a resulting pack by grabbing the nth element of the original pack.
filter_seq takes a template<size_t> functor and a seq, and uses the functor to filter the elements of the seq. The functor can return either a ::value of type size_t or a ::result of type seq<...> or neither, allowing one-to-one or one-to-many functors.
A few other helper functions, like conc, append, conc_pack, get_indexes, make_seq, nth_type round things out.
I tested it with filter_zero_out which is a ::value based filter that removes 0, and filter_zero_out_b which is a ::result based filter that also removes 0.
For silly reasons I'll not go into here, I need the commented out line to work and the line above it it to not work:
template<uint _N, typename... _Args>
struct PartialTuple;
template<uint _N, typename _Arg, typename... _Args>
struct PartialTuple<_N, _Arg, _Args...>: PartialTuple<_N-1, _Args...> {};
template<typename _Arg, typename... _Args>
struct PartialTuple<0, _Arg, _Args...>
{
typedef std::tuple<_Arg, _Args...> type;
};
int main()
{
// I want this to not work...
PartialTuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};
// I want this to work...
//PartialTuple<1, std::string, std::string, int, int>::type B{"test", "test", 5};
}
I tried swapping _Arg with _Args..., but that won't compile (at least in GCC 4.6):
error: parameter pack argument ‘_Args ...’ must be at the end of the template argument list
How can I pull items off from the tail instead of from the head?
Here's a solution: Instead of truncating N from the back, I just truncate sizeof...(Args) - N from the front:
#include <tuple>
/* Concatenator helper */
template <typename T, typename Tuple> struct cat;
template <typename T, typename ...Args>
struct cat<T, std::tuple<Args...>>
{
typedef typename std::tuple<T, Args...> value;
};
/* Head-of-tuple */
template <unsigned int, typename...> struct tuple_head;
// Base case. Need to specialize twice, once for one and once for variadic types
template <typename ...Args>
struct tuple_head<0, Args...>
{
typedef std::tuple<> value;
};
template <typename T>
struct tuple_head<0, T>
{
typedef std::tuple<> value;
};
// Recursion step
template <unsigned int N, typename T, typename ...Args>
struct tuple_head<N, T, Args...>
{
typedef typename cat<T, typename tuple_head<N - 1, Args...>::value>::value value;
};
/* User interface */
template <unsigned int N, typename ...Args>
struct PartialTuple
{
typedef typename tuple_head<sizeof...(Args) - N, Args...>::value type;
};
/* Usage */
#include <string>
int main()
{
// I want this to not work...
//PartialTuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};
// I want this to work...
PartialTuple<1, std::string, std::string, int, int>::type B("test", "test", 5);
PartialTuple<0, std::string, std::string, int, int>::type C("test", "test", 5, 6);
}
I've been playing with it all night and finally got something to work (changed my casing to match the STL):
template<uint _N, typename... _All>
struct reverse_tuple_outer
{
template<typename _Head, typename... _Tail>
struct reverse_tuple_inner: reverse_tuple_outer<_N-1, _Head, _All...>::template reverse_tuple_inner<_Tail...> { };
};
template<typename... _All>
struct reverse_tuple_outer<0, _All...>
{
template<typename... _Tail>
struct reverse_tuple_inner {
typedef std::tuple<_All...> type;
};
};
template<typename... _Args>
struct reverse_tuple
{
typedef typename reverse_tuple_outer<sizeof...(_Args)>::template reverse_tuple_inner<_Args...>::type type;
};
template<typename... _Args>
struct strip_and_reverse_tuple;
template<typename... _Args>
struct strip_and_reverse_tuple<std::tuple<_Args...>>
{
typedef typename reverse_tuple<_Args...>::type type;
};
template<uint _N, typename... _Args>
struct partial_tuple
{
typedef typename strip_and_reverse_tuple<typename reverse_tuple_outer<sizeof...(_Args)-_N>::template reverse_tuple_inner<_Args...>::type>::type type;
};
int main()
{
//partial_tuple<1, std::string, std::string, int, int>::type A{"test", 5, 1};
partial_tuple<1, std::string, std::string, int, int>::type B{"test", "test", 5};
}
As an added bonus, I also have reverse_tuple, should I ever need it.
I've made my code work a little bit like lists in Haskell - because, well, TMP is purely functional language inside C++.
add_to_pack is equivalent to Haskell's list constructor (:). drop_from_end is implemented as (in Haskell notation) \x list -> take (length list - x) list, where take n just takes first n elements of the list.
I suppose you could use std::tuple directly instead of pack, but I liked this solution better, because it doesn't misuse tuple as template parameter pack holder. :)
Here's the code:
#include <tuple>
#include <type_traits> // for std::conditional
template <typename... Pack>
struct pack
{ };
template <typename, typename>
struct add_to_pack;
template <typename A, typename... R>
struct add_to_pack<A, pack<R...>>
{
typedef pack<A, R...> type;
};
template <typename>
struct convert_to_tuple;
template <typename... A>
struct convert_to_tuple<pack<A...>>
{
typedef std::tuple<A...> type;
};
template <int, typename...>
struct take;
template <int N>
struct take<N>
{
typedef pack<> type;
};
template <int N, typename Head, typename... Tail>
struct take<N, Head, Tail...>
{
typedef
typename std::conditional<
(N > 0),
typename add_to_pack<
Head,
typename take<
N - 1,
Tail...
>::type
>::type,
pack<>
>::type type;
};
template <int N, typename... A>
struct drop_from_end
{
// Add these asserts if needed.
//static_assert(N >= 0,
// "Cannot drop negative number of elements!");
//static_assert(N <= static_cast<int>(sizeof...(A)),
// "Cannot drop more elements than size of pack!")
typedef
typename convert_to_tuple<
typename take<
static_cast<int>(sizeof...(A)) - N,
A...
>::type
>::type type;
};
int main()
{
drop_from_end<2, const char*, double, int, int>::type b{"pi", 3.1415};
}
And here's the code at work: via ideone.com.
The take struct is more or less equivalent to following Haskell code:
take n [] = []
take n (x:xs)
| n > 0 = x : take (n - 1) xs
| otherwise = []
I have done something similar using Boost.MPL and Boost.Fusion: compute the type sequence using the MPL facilities such as push_back, then convert it to a fusion::vector with fusion::as_vector and MPL adaptors. I already had a helper to convert a fusion::vector to std::tuple though.