Generating parameter list at compile time [duplicate] - c++

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>...>;
};

Related

Specific types from a pack based on position

take_from_args<foo<int, bool, char, float>, 0,2>::type is to be
foo<int, char> based on the positions 0 and 2. The implementation is easy:
template <typename Class, std::size_t... Positions>
struct take_from_args;
template <template <typename...> class P, typename... Ts, std::size_t... Is>
struct take_from_args<P<Ts...>, Is...> {
using type = P<std::tuple_element_t<Is, std::tuple<Ts...>>...>;
};
Now, let's try to apply this to this class:
template <int V, bool B, typename... Args>
struct bar {};
The problem is the int and bool parameters of bar, so it cannot be passed into take_from_args. So let's define:
template <int V, bool B>
struct bar_h {
template <typename... Args>
using templ = bar<V, B, Args...>;
};
Unfortunately, take_from_args<bar_h<5, true>::templ<int, bool, char, float>, 0,2>::type won't compile. How do I redefine take_from_args so that it can template classes like bar?
My entire code:
#include <tuple>
template <typename Class, std::size_t... Positions> struct take_from_args;
template <template <typename...> class P, typename... Ts, std::size_t... Is>
struct take_from_args<P<Ts...>, Is...> {
using type = P<std::tuple_element_t<Is, std::tuple<Ts...>>...>;
};
// Testing
template <typename... Args>
struct foo {};
template <int V, bool B, typename... Args>
struct bar {};
template <int V, bool B>
struct bar_h {
template <typename... Args>
using templ = bar<V, B, Args...>;
};
int main() {
static_assert(std::is_same<
take_from_args<foo<int, bool, char, float>, 0,2>::type,
foo<int, char>>::value);
// static_assert(std::is_same<
// take_from_args<bar_h<5, true>::templ<int, bool, char, float>, 0,2>::type,
// bar<5, true, int, char>>::value);
}
Your workaround with bar_h does not work because bar_h<5, true>::templ<int, bool, char, float> is just an alias for bar<5, true, int, bool, char, float>:
static_assert(// compiles without error
std::is_same_v<
bar_h<5, true>::templ<int, bool, char, float>,
bar<5, true, int, bool, char, float>
>
);
I see two options:
1. avoid non-type template arguments in `bar`
see std::integral_constant
Edit: As you followed this approach in your answer, but I experience issues with clang: Here is a modified version which works for me with GCC 7.2 and Clang 5.0.
template<auto...> struct Vals {};
template<class T>
struct HasVals : std::false_type {};
template<auto... Vs>
struct HasVals<Vals<Vs...>> : std::true_type {};
template<class T, size_t... is>
struct take_from_args;
template<template<class...> class P, class... Ts, size_t... is>
struct take_from_args<P<Ts...>, is...> {
// convention: pass-through first argument if it `HasVals`
using Vs = std::tuple_element_t<0, std::tuple<Ts...>>;
using type = std::conditional_t<
HasVals<Vs>::value,
P<Vs, std::tuple_element_t<1u+is, std::tuple<Ts...>>...>,
P<std::tuple_element_t<is, std::tuple<Ts...>>...>
>;
};
// Testing
template<class Vs, class... Args>
struct bar;
template<int v, bool b, class... Args>
struct bar<Vals<v, b>, Args...> {
static constexpr int value = v;
static constexpr bool truth = b;
};
2. provide more specialization for `take_from_args`
Since you specifically ask for the latter, here is an example:
// there could be 1 value(s) at the beginning...
template<
template<auto, auto, typename...> class P,
auto v0, class... Ts, std::size_t... is
> struct take_from_args<P<v0, Ts...>, is...> {
using type = P<v0, std::tuple_element_t<is, std::tuple<Ts...>>...>;
};
// ... 2 ...
template<
template<auto, auto, typename...> class P,
auto v0, auto v1, class... Ts, std::size_t... is
> struct take_from_args<P<v0, v1, Ts...>, is...> {
using type = P<v0, v1, std::tuple_element_t<is, std::tuple<Ts...>>...>;
};
// ... 3 ... and more?
template<
template<auto, auto, typename...> class P,
auto v0, auto v1, auto v2, class... Ts, std::size_t... is
> struct take_from_args<P<v0, v1, v2, Ts...>, is...> {
using type = P<v0, v1, v2, std::tuple_element_t<is, std::tuple<Ts...>>...>;
};
Unfortunately, I had no success using auto... for the deduction of leading template arguments.
Following Julius' last suggestion for redesigning the class bar:
#include <tuple>
#include <type_traits>
template <auto...> struct Vals {};
template <typename Class> struct HasVals : std::false_type {};
template <auto... Vs>
struct HasVals<Vals<Vs...>> : std::true_type {};
template <typename Class, std::size_t... Positions> struct take_from_args;
template <template <typename, typename...> class P, typename VALS, typename... Ts, std::size_t... Is>
struct take_from_args<P<VALS, Ts...>, Is...> {
using type = std::conditional_t<HasVals<VALS>::value,
P<VALS, std::tuple_element_t<Is, std::tuple<Ts...>>...>,
P<std::tuple_element_t<Is, std::tuple<VALS, Ts...>>...> // VALS is part of the tuple in this case
>;
};
// Testing
template <typename... Args>
struct foo {};
template <typename VALS, typename... Args>
struct bar;
template <int v, bool b, typename... Args>
struct bar<Vals<v, b>, Args...> {
static constexpr int value = v;
static constexpr bool truth = b;
};
int main() {
static_assert(std::is_same<
take_from_args<foo<int, bool, char, float>, 0,2>::type,
foo<int, char>>::value);
using new_bar = take_from_args<bar<Vals<5, true>, int, bool, char, float>, 0,2>::type;
static_assert(std::is_same<
new_bar,
bar<Vals<5, true>, int, char>>::value);
static_assert(new_bar::value == 5);
static_assert(new_bar::truth == true);
//static_assert(std::is_same<decltype(new_bar::value), int>::value); // why fails?
//static_assert(std::is_same<decltype(new_bar::truth), bool>::value);
}

Creating a sub-tuple starting from a std::tuple<some_types...>

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.

Removing the first type of a std::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.

Transform tuple to "triangular" tuple

How do I transform this type:
std::tuple<T0, T1, ..., TN1, TN>
into this:
std::tuple<
std::function<T0()>,
std::function<T1(T0)>,
std::function<T2(T0, T1)>,
...
std::function<TN(T0, ..., TN1 )>
>
Right ... isn't suffice, but you could always use pattern matching (i.e. partial specialization) with recursion:
#include <tuple>
#include <functional>
#include <cstdlib>
// A type to store list of integers
template <size_t... ns>
struct integers
{
template <size_t n>
using push_back = integers<ns..., n>;
};
// This generates 'integers<0, 1, 2, ..., n-1>'
template <size_t n>
struct iota
{
typedef typename iota<n-1>::type::template push_back<n-1> type;
};
template <>
struct iota<0>
{
typedef integers<> type;
};
// Put a type to the front of the argument list
template <typename T, typename U>
struct push_front;
template <typename T, typename R, typename... A>
struct push_front<R(A...), T>
{
typedef R type(T, A...);
};
// This converts 'std::tuple<T0, T1, ..., TN>' to the function type
// 'TK(T0, T1, ..., TK-1)' where K is the first parameter
template <size_t, typename...>
struct slice;
template <size_t end, typename First, typename... Rest>
struct slice<end, First, Rest...>
{
typedef typename push_front<typename slice<end-1, Rest...>::type, First>::type type;
};
template <typename First, typename... Rest>
struct slice<0, First, Rest...>
{
typedef First type();
};
// This calls 'slice' on T... for all integers in the list.
template <typename T, typename U>
struct triangularize_impl;
template <typename... T, size_t... n>
struct triangularize_impl<std::tuple<T...>, integers<n...>>
{
typedef std::tuple<std::function<typename slice<n, T...>::type>...> type;
};
// This is a wrapper of 'triangularize_impl'.
template <typename T>
struct triangularize;
template <typename... T>
struct triangularize<std::tuple<T...>>
{
typedef typename triangularize_impl<std::tuple<T...>, typename iota<sizeof...(T)>::type>::type type;
};
As a demo, in g++ 4.7 when we write
triangularize<std::tuple<int, float, double, char>>::type d = 0;
the error message shows
error: conversion from ‘int’ to non-scalar type
‘triangularize<std::tuple<int, float, double, char> >::type {aka
std::tuple<std::function<int()>,
std::function<float(int)>,
std::function<double(int, float)>,
std::function<char(int, float, double)> >}’
requested
Showing the code is correct.

How can I pull variadic template arguments off from the tail instead of the head?

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.