I'm trying to split an index_sequence into two halves. For that, I generate an index_sequence with the lower half and use it to skip the leading elements on the full index_sequence. The following is a minimal test case that represents what I'm trying to achieve:
template <int ...I>
struct index_sequence {};
template <int ...I, int ...J>
void foo(index_sequence<I...>, index_sequence<I..., J...>)
{}
int main()
{
foo(index_sequence<0>{}, index_sequence<0, 1>{});
}
I've tried this with the latest versions of Clang, GCC and MSVC, and they all fail to deduce J.... Is this allowed by the standard? If not, why and what would be a good way to achieve my intent?
If what you want is to split a std::index_sequence instead of removing the common prefix of two std::index_sequences, I think you can benefit from an implementation of slice and using it to split a std::index_sequence into pieces.
I'm going to omit the implementation of std::index_sequence and friends, since you can refer to the paper, N3658, and a sample implementation here.
make_index_range
To implement slice, we'll use a helper called make_integer_range. We want a std::index_sequence generator which gives us [Begin, End) instead of [0, End). Leveraging std::make_integer_sequence, we get:
template <typename T, typename Seq, T Begin>
struct make_integer_range_impl;
template <typename T, T... Ints, T Begin>
struct make_integer_range_impl<T, std::integer_sequence<T, Ints...>, Begin> {
using type = std::integer_sequence<T, Begin + Ints...>;
};
/* Similar to std::make_integer_sequence<>, except it goes from [Begin, End)
instead of [0, End). */
template <typename T, T Begin, T End>
using make_integer_range = typename make_integer_range_impl<
T, std::make_integer_sequence<T, End - Begin>, Begin>::type;
/* Similar to std::make_index_sequence<>, except it goes from [Begin, End)
instead of [0, End). */
template <std::size_t Begin, std::size_t End>
using make_index_range = make_integer_range<std::size_t, Begin, End>;
slice
Since we don't have a std::get-like functionality for std::index_sequence or a variadic template pack, we just build a temporary std::array to get us std::get. Then explode the array with only the slice we want.
template <std::size_t... Indices, std::size_t... I>
constexpr decltype(auto) slice_impl(
std::index_sequence<Indices...>,
std::index_sequence<I...>) {
using Array = std::array<std::size_t, sizeof...(Indices)>;
return std::index_sequence<std::get<I>(Array{{Indices...}})...>();
}
template <std::size_t Begin, std::size_t End, std::size_t... Indices>
constexpr decltype(auto) slice(std::index_sequence<Indices...> idx_seq) {
return slice_impl(idx_seq, make_index_range<Begin, End>());
}
split_at
One example of using the slice we just built is to write a split_at function. We specify the index at which we want to split the std::index_sequence, and return a pair of std::index_sequences split at the given index.
template <std::size_t At, std::size_t... Indices>
constexpr decltype(auto) split_at(index_sequence<Indices...> idx_seq) {
return std::make_pair(slice<0, At>(idx_seq),
slice<At, sizeof...(Indices)>(idx_seq));
}
Examples of split_at:
static_assert(std::is_same<
decltype(split_at<2>(index_sequence<1, 4, 2>())),
std::pair<index_sequence<1, 4>, index_sequence<2>>>(), "");
static_assert(std::is_same<
decltype(split_at<1>(index_sequence<1, 4, 2, 3>())),
std::pair<index_sequence<1>, index_sequence<4, 2, 3>>>(), "");
14.8.2.5/9 ... If the template argument list of P contains a pack expansion that is not the last template argument, the entire template argument list is a non-deduced context...
Thus, when comparing index_sequence<I..., J...> with index_sequence<0, 1>{}, neither I... nor J... can be deduced.
To get suffix, you may use something like:
template<int ... I> struct get_suffix_helper {
template<int ... J> static index_sequence<J...> foo(index_sequence<I..., J...>);
};
template<typename T1, typename T2> struct get_suffix;
template<int ... Is1, int ... Is2>
struct get_suffix<index_sequence<Is1...>, index_sequence<Is2...>> :
public decltype(get_suffix_helper<Is1...>::foo(std::declval<index_sequence<Is2...>>())) {};
static_assert(std::is_base_of<index_sequence<>,
get_suffix<index_sequence<1, 2>,
index_sequence<1, 2>>>::value, "error");
static_assert(std::is_base_of<index_sequence<42>,
get_suffix<index_sequence<1, 2>,
index_sequence<1, 2, 42>>>::value, "error");
Or, with some error check:
template <typename T1, typename T2> struct get_suffix;
template<int ...Is>
struct get_suffix<index_sequence<>, index_sequence<Is...>>
{
typedef index_sequence<Is...> type;
static const bool valid = true;
};
template<int ...Is>
struct get_suffix<index_sequence<Is...>, index_sequence<>>
{
typedef void type;
static const bool valid = false;
};
template<>
struct get_suffix<index_sequence<>, index_sequence<>>
{
typedef index_sequence<> type;
static const bool valid = true;
};
template<int N, int ...Is, int... Js>
struct get_suffix<index_sequence<N, Is...>, index_sequence<N, Js...>>
{
typedef typename get_suffix<index_sequence<Is...>, index_sequence<Js...>>::type type;
static const bool valid = get_suffix<index_sequence<Is...>, index_sequence<Js...>>::valid;
};
template<int N1, int N2, int ...Is, int... Js>
struct get_suffix<index_sequence<N1, Is...>, index_sequence<N2, Js...>>
{
typedef void type;
static const bool valid = false;
};
static_assert(std::is_same<index_sequence<>,
get_suffix<index_sequence<1, 2>,
index_sequence<1, 2>>::type>::value, "error");
static_assert(!get_suffix<index_sequence<1, 2, 42>, index_sequence<1, 2>>::valid, "error");
static_assert(!get_suffix<index_sequence<0, 2, 1>, index_sequence<0, 1, 2>>::valid, "error");
static_assert(std::is_same<index_sequence<42>,
get_suffix<index_sequence<1, 2>,
index_sequence<1, 2, 42>>::type>::value, "error");
Not an answer, but a workaround: recursively trim off the leading elements a la:
template <typename, typename> struct remove_prefix;
template <std::size_t... I>
struct remove_prefix<index_sequence<>, index_sequence<I...>> {
using type = index_sequence<I...>;
};
template <std::size_t First, std::size_t... I, std::size_t... J>
struct remove_prefix<index_sequence<First, I...>,
index_sequence<First, J...>> {
using type = typename remove_prefix<index_sequence<I...>,
index_sequence<J...>>::type;
};
Demo at Coliru.
I needed to split an index_sequence into a head and tail at a particular point and this was the implementation that I came up with:
template<size_t N, typename Lseq, typename Rseq>
struct split_sequence_impl;
template<size_t N, size_t L1, size_t...Ls, size_t...Rs>
struct split_sequence_impl<N,index_sequence<L1,Ls...>,index_sequence<Rs...>> {
using next = split_sequence_impl<N-1,index_sequence<Ls...>,index_sequence<Rs...,L1>>;
using head = typename next::head;
using tail = typename next::tail;
};
template<size_t L1, size_t...Ls, size_t...Rs>
struct split_sequence_impl<0,index_sequence<L1,Ls...>,index_sequence<Rs...>> {
using tail = index_sequence<Ls...>;
using head = index_sequence<Rs...,L1>;
};
template<typename seq, size_t N>
using split_sequence = split_sequence_impl<N-1,seq,empty_sequence>;
template<typename seq, size_t N>
using sequence_head_t = typename split_sequence<seq,N>::head;
template<typename seq, size_t N>
using sequence_tail_t = typename split_sequence<seq,N>::tail;
Related
It is possible to use template parameters pack as follows:
template <int T1, int... Ts>
struct Test {
static constexpr int sizes[] = {Ts...};
};
template <int T1, int... Ts>
constexpr int Test<T1, Ts...>::sizes[];
However, as it is detailed here, the template parameter pack must be the last template parameter. Hence, we cannot have a code such as this:
template <int T1, int... Ts, int Tn>
struct Test {
static constexpr int sizes[] = {Ts...};
Foo<Ts...> foo;
};
template <int T1, int... Ts, int Tn>
constexpr int Test<T1, Ts..., Tn>::sizes[];
In many cases, we need to have access to the last element of a set of template parameters. My question is, what's the best practice for realizing the above code?
Edit:
This is not duplicate of this question. I am trying to get everything except the last parameter (not the last parameter itself), since I need to define Foo as follows:
Foo<Ts...> foo;
You could go with the standard method of using std::index_sequence
template<template<auto...> typename Tmp, size_t... Is, typename... Args>
constexpr auto take_as(std::index_sequence<Is...>, Args...)
{
using Tup = std::tuple<Args...>;
return Tmp<std::tuple_element_t<Is, Tup>{}...>{};
}
template<auto... Vals>
struct except_last
{
template<template<auto...> typename Tmp>
using as = decltype(take_as<Tmp>(std::make_index_sequence<sizeof...(Vals) - 1>{},
std::integral_constant<decltype(Vals), Vals>{}...));
};
Which you use as
using F = except_last<1, 2, 3, 4>::as<Foo>; // F is Foo<1, 2, 3>
This is both easier to implement and read, but you potentially get O(n) instantiation depth. If you are obsessed with efficiency, you could do O(1) instantiation depth by abusing fold expressions
template<typename T>
struct tag
{
using type = T;
};
template<typename F, typename... Ts>
using fold_t = decltype((F{} + ... + tag<Ts>{}));
template<size_t N, typename... Ts>
struct take
{
template<typename T>
auto operator+(tag<T>) -> take<N - 1, Ts..., T>;
};
template<typename... Ts>
struct take<0, Ts...>
{
template<template<auto...> typename Tmp>
using as = Tmp<Ts{}...>;
template<typename T>
auto operator+(tag<T>) -> take<0, Ts...>;
};
template<auto... Vals>
struct except_last
{
template<template<auto...> typename Tmp>
using as = fold_t<take<sizeof...(Vals) - 1>,
std::integral_constant<decltype(Vals), Vals>...>::template as<Tmp>;
};
What's the most efficient way to access the last template parameter?
You could use a little helper to convert the parameter pack into an array.
template<int... Args>
struct pack {
static constexpr std::array as_array{ Args... };
};
You can then get the last argument with array indexing:
template <int T1, int... Ts>
struct Test {
static constexpr int last = pack<Ts...>::as_array[sizeof...(Ts) - 1];
integer_sequence is a way:
template <typename SeqN, typename Seq> struct TestImpl;
template <int... Ns, std::size_t ... Is>
struct TestImpl<std::integer_sequence<int, Ns...>, std::index_sequence<Is...>>
{
private:
using SeqTuple = std::tuple<std::integral_constant<int, Ns>...>;
public:
static constexpr int sizes[] = {std::tuple_element_t<Is, SeqTuple>::value...};
Foo<std::tuple_element_t<Is, SeqTuple>::value...> foo;
};
template <int N1, int N2, int... Ns> // At least 2 numbers
using Test = TestImpl<std::integer_sequence<int, N1, N2, Ns...>,
std::make_index_sequence<1 + sizeof...(Ns)>>;
Demo
I have the data structure:
template <int...I> struct index {};
template <typename...T> struct data {};
template <int I, int J> struct X
{
static constexpr int i = I;
static constexpr int j = J;
};
typedef data< X<0,4>, X<1,2>, X<2,1>, X<1,6>, X<1,3> > data_t;
Where data does not contain duplicates and the indices J are small, in the range 0-31.
I want to create a static index which contains the position in data of all X with index I equal to some given value (e.g. I=1), sorted by index J. It is the "sorting" bit which I find difficult.
For example, I would like to implement a class build_index such that:
typedef build_index<1,data>::type_t index_t;
generates the same as:
typedef index<1, 4, 3> index_t;
which reflects the positions in data of the elements X(1,J) ordered by J:
X(1,2) at data(1), X(1,3) at data(4), X(1,6) at data(3)
I would prefer not to use the STL, as it is not available for gcc-avr, although I could port selected snippets.
When you are facing something complex in C++ template programming, it mostly helps to try to break it into several smaller steps (like with most programming problems). Here is a possible path:
Select the X which match the selected I and store them in a new data type replacing the I with the position (use recursion for this)
Sort the X in the selected_data by J. This is a bit annoying to write, I think. Maybe you should create a separate question for that.
Extract the positions from the sorted and selected X
And here is the corresponding code. I am using std::conditional, but that is easy to replace of course. I am using std::is_same in the tests, you do not really need that of course (and it would be trivial to implement otherwise).
Your stuff + utility header for std::conditional and std::is_same
#include <utility>
template <int... I>
struct index
{
};
template <typename... T>
struct data
{
};
template <int I, int J>
struct X
{
static constexpr int i = I;
static constexpr int j = J;
};
typedef data<X<0, 4>, X<1, 2>, X<2, 1>, X<1, 6>, X<1, 3>> data_t;
Extract the Xs that match the I we are looking for and replace the is with the position.
template <int Pos, int I, typename Extracted, typename Rest>
struct ExtractImpl;
template <int Pos, int I, typename... ExtractedX>
struct ExtractImpl<Pos, I, data<ExtractedX...>, data<>>
{
using type = data<ExtractedX...>;
};
template <int Pos, int I, typename... ExtractedX, typename T, typename... Rest>
struct ExtractImpl<Pos, I, data<ExtractedX...>, data<T, Rest...>>
{
using type = typename std::conditional<
(T::i == I),
typename ExtractImpl<Pos + 1,
I,
data<ExtractedX..., X<Pos, T::j>>,
data<Rest...>>::type,
typename ExtractImpl<Pos + 1, I, data<ExtractedX...>, data<Rest...>>::
type>::type;
};
template <int I, typename Data>
struct Extract
{
using type = typename ExtractImpl<0, I, data<>, Data>::type;
};
using extracted = typename Extract<1, data_t>::type;
static_assert(std::is_same<extracted, data<X<1, 2>, X<3, 6>, X<4, 3>>>::value, "");
Sort by J. This is done by incrementally inserting elements into a sorted list. There might be more elegant ways to do it.
template <typename T, typename LessList, typename RestList>
struct insert_impl;
template <typename T, typename... Lesser>
struct insert_impl<T, data<Lesser...>, data<>>
{
using type = data<Lesser..., T>;
};
template <typename T, typename... Lesser, typename Next, typename... Rest>
struct insert_impl<T, data<Lesser...>, data<Next, Rest...>>
{
using type = typename std::conditional<
(T::j < Next::j),
data<Lesser..., T, Next, Rest...>,
typename insert_impl<T, data<Lesser..., Next>, data<Rest...>>::type>::
type;
};
template <typename T, typename SortedList>
struct insert
{
using type = typename insert_impl<T, data<>, SortedList>::type;
};
template <typename SortedList, typename UnsortedList>
struct SortImpl;
template <typename SortedList>
struct SortImpl<SortedList, data<>>
{
using type = SortedList;
};
template <typename SortedList, typename T, typename... UnsortedX>
struct SortImpl<SortedList, data<T, UnsortedX...>>
{
using type = typename SortImpl<typename insert<T, SortedList>::type,
data<UnsortedX...>>::type;
};
template <typename UnsortedList>
struct Sort
{
using type = typename SortImpl<data<>, UnsortedList>::type;
};
using sorted = typename Sort<extracted>::type;
static_assert(std::is_same<sorted, data<X<1, 2>, X<4, 3>, X<3, 6>>>::value, "");
Finally, extract the indexes you are looking for:
template <typename List>
struct Indexes;
template <typename... Data>
struct Indexes<data<Data...>>
{
using type = index<Data::i...>;
};
using result = typename Indexes<sorted>::type;
static_assert(std::is_same<result, index<1, 4, 3>>::value, "");
Word of warning: While I don't see any problems in the code, I have not tested it beyond your example...
I'll share my approach to this problem which I think is kind of neat. I used C++11 std::conditional, and C++14 std::integer_sequence and std::make_integer_sequence, all of which you can find implementations of online.
Let's start with the data structures you had.
template <int... Is> struct index {};
template <typename... Ts> struct list {};
template <int L, int R> struct pair {};
We'll use a metafunction concat, which concatenates N list of types. We use it to filter a list by returning list<T> when the predicate returns true, and list<> otherwise.
For example, to filter the even numbers from list<1, 3, 2, 4, 2>, we can perform std::conditional_t<I % 2 == 0, list<I>, list<>> for each I to get concat_t<list<>, list<>, list<2>, list<4>, list<2>> = list<2, 4, 2>.
template <typename... Ts> struct concat;
template <> struct concat<> { using type = list<>; }
template <typename... Ts>
struct concat<list<Ts...>> { using type = list<Ts...>; };
template <typename... Ts, typename... Us>
struct concat<list<Ts...>, list<Us...>> { using type = list<Ts..., Us...>; };
template <typename... Ts, typename... Us, typename... Tail>
struct concat<list<Ts...>, list<Us...>, Tail...>
: concat<list<Ts..., Us...>, Tail...> {};
template <typename... Ts>
using concat_t = typename concat<Ts...>::type;
Now we get to build_index. We perform bucket sort within the known range of [0, 32). We could have used a generic sorting algorithm, but it was more fun to cheat.
template <int N, typename T> struct build_index;
// e.g., `build_index<
// 1, list<pair<0, 4>, pair<1, 2>, pair<2, 1>, pair<1, 6>, pair<1, 3>>`
template <int N, int... Ls, int... Rs>
struct build_index<N, list<pair<Ls, Rs>...>> {
// Filter for pairs where `N == lhs`, and replace the `lhs` with the index.
template <int... Is>
static auto filter(std::integer_sequence<int, Is...>)
-> concat_t<std::conditional_t<N == Ls, list<pair<Is, Rs>>, list<>>...>;
// e.g., `list<pair<1, 2>, pair<3, 6>, pair<4, 3>>`
using filtered =
decltype(filter(std::make_integer_sequence<int, sizeof...(Ls)>{}));
// `get<I>(set)` returns the `lhs` if `set` can implicitly convert to
// `pair<lhs, I>` for some `lhs`, and nothing otherwise.
template <typename... Ts> struct set : Ts... {};
template <int I, int L> static list<index<L>> get(pair<L, I>);
template <int I> static list<> get(...);
// We invoke `get<I>` for `I` in `[0, 32)` to sort `filtered`.
template <int... Is, typename... Ts>
static auto sort(std::integer_sequence<int, Is...>, list<Ts...>)
-> concat_t<decltype(get<Is>(set<Ts...>{}))...>;
// e.g., `list<index<1>, index<4>, index<3>>`
using sorted =
decltype(sort(std::make_integer_sequence<int, 32>{}, filtered{}));
// e.g., `list<1, 4, 3>`
template <int... Is> static index<Is...> indices(list<index<Is>...>);
using type = decltype(indices(sorted{}));
};
template <int N, typename... Ts>
using build_index_t = typename build_index<N, Ts...>::type;
With which we get:
using index_t = build_index_t<
1, list<pair<0, 4>, pair<1, 2>, pair<2, 1>, pair<1, 6>, pair<1, 3>>>;
static_assert(std::is_same<index<1, 4, 3>, index_t>::value, "");
As the title says I've got a Variadic Template that accepts at least 3 parameters (int's)
template<int p_first, int p_second, int p_third, int... p_rest>
and i need to split these into first, middle and last
class MyClass {
OtherClass<p_first> first;
// Works obviously
// std::vector<OtherClass> middle... Doesn't work
OtherClass<p_last> last;
// No idea how to do this
}
Visual Studio 2015 C++ features are available
Edit: Sorry I forgot to mention key aspects.
OtherClass implementation:
template <int v>
class OtherClass { ... };
Yes, I want all the values in the middle (between first and last)
"the vector can't hold different types of OtherClass" Thank you for reminding me of that. This might be the reason, that this isn't possible.
"Are you reinventing tuples?" Maybe using tuples is really a better solution. Looking into it.
Thank you for the posts. I'll understand, test the code and comment soon!
First, boilerplate. I'm working in types, not constants, because metaprogramming with types is far easier.
template<class T>struct tag{using type=T;};
template<class Tag>using type_t=typename Tag::type;
template<int I>using int_k=std::integral_constant<int, I>;
template<class...>struct types{using type=types;};
template<template<class...>class Z, class pack>
struct apply;
template<template<class...>class Z, class pack>
using apply_t=type_t<apply<Z,pack>>;
template<template<class...>class Z, class...Ts>
struct apply<Z, types<Ts...>>:tag<Z<Ts...>>{};
Now once we have a pack of middle elements, we can apply them.
template <std::size_t N, class... Ts>
using get_t = type_t< std::tuple_element< N, std::tuple<Ts...> > >;
gets the nth type from a list of types.
template <class Is, class pack>
struct get_slice;
template <class Is, class pack>
using get_slice_t=type_t<get_slice<Is,pack>>;
template<std::size_t...Is, class...Ts>
struct get_slice<std::index_sequence<Is...>,types<Ts...>>:
types< get_t<Is, Ts...>... >{};
This lets us take a pack, and get a slice from it.
Offset an index sequence:
template<class Is, std::size_t I>
struct offset;
template<class Is, std::size_t I>
using offset_t=type_t<offset<Is,I>>;
template<std::size_t...Is, size_t I>
struct offset<std::index_sequence<Is...>, I>:
tag<std::index_sequence<(I+Is)...>>
{};
Extract the middle elements starting at start of length len:
template<std::size_t start, std::size_t len, class pack>
struct get_mid:
get_slice< offset_t< std::make_index_sequence<len>, start >, pack >
{};
template<std::size_t start, std::size_t len, class pack>
using get_mid_t=type_t<get_mid<start,len,pack>>;
and now we can split your elements into first, last and stuff the rest in a tuple:
template<int p_first, int p_second, int p_third, int...is>
class MyClass {
using pack = types< int_k<p_first>, int_k<p_second>, int_k<p_third>, int_k<is>... >;
OtherClass<p_first> first;
using mid = get_mid_t<1, sizeof...(is)+1, pack >;
template<class...Ts>
using OtherClass_tuple = std::tuple<OtherClass<Ts::value>...>;
apply_t< OtherClass_tuple, mid > middle;
OtherClass<get_t<sizeof...(is)+2, pack>::value> last;
};
You could write a helper struct to get the Nth int from an int pack:
template <std::size_t N, int... I>
struct get_n :
std::integral_constant<int,
std::get<N>(std::array<int,sizeof...(I)> { I... })
>
{};
Then you could write metafunctions to get the middle and end:
template <int... I>
using get_middle = get_n<sizeof...(I)/2 - 1, I...>;
template <int... I>
using get_end = get_n<sizeof...(I) - 1, I...>;
You can use this like so:
using p_last = get_end<p_third, p_rest...>;
OtherClass<p_last> last;
If you want a tuple of OtherClass<N> for all the middle elements, here's a fairly simple solution. See Yakk's answer for a more complex, flexible one.
template <template <int> class ToBuild, class Seq, int... Args>
struct build_tuple;
template <template <int> class ToBuild, std::size_t... Idx, int... Args>
struct build_tuple<ToBuild, std::index_sequence<Idx...>, Args...> {
using type = std::tuple<ToBuild<get_n<Idx, Args...>::value>...>;
};
template<int p_first, int p_second, int p_third, int... p_rest>
struct MyClass {
MyClass() {
typename build_tuple<OtherClass,
std::make_index_sequence<sizeof...(p_rest) + 1>,
p_second, p_third, p_rest...>::type middle;
}
};
Use Boost.Hana (requires a C++14 compiler):
#include <boost/hana.hpp>
#include <tuple>
namespace hana = boost::hana;
template <int>
struct OtherClass { };
template <int ...I>
class MyClass {
static constexpr auto ints = hana::tuple_c<int, I...>;
OtherClass<hana::front(ints)> first;
using Middle = typename decltype(
hana::unpack(hana::slice_c<1, sizeof...(I) - 1>(ints), [](auto ...i) {
return hana::type_c<std::tuple<OtherClass<ints[i]>...>>;
})
)::type;
Middle middle;
OtherClass<hana::back(ints)> last;
};
int main() {
MyClass<0, 3, 2, 1> x;
}
Note that I'm abusing a Clang bug in the above, because lambdas are not allowed to appear in unevaluated contexts. To be standards-compliant, you should use a hand-written function object instead of the lambda when calling hana::unpack.
Also, if you go the Hana way, I would suggest that you use hana::tuple if compile-time performance is a consideration, since std::tuple is a slowpoke in all known standard library implementations.
Since the definition of OtherClass is unknown and the vector can't hold different types of OtherClass i assumed following code:
The code uses recursive template inheritance with integer_sequence to split the int pack.
#include <utility>
#include <vector>
template<int...>
struct OtherClass { };
template<typename, int... >
struct Splitter;
template<int... middle, int next, int... rest>
struct Splitter<std::integer_sequence<int, middle...>, next, rest...>
: Splitter<std::integer_sequence<int, middle..., next>, rest...> { };
template<int... middle, int last>
struct Splitter<std::integer_sequence<int, middle...>, last>
{
static std::vector<int> const& get_vector()
{
static std::vector<int> const m = { middle... };
return m;
}
using last_t = std::integral_constant<int,
last
>;
};
template<int p_first, int p_second, int p_third, int... p_rest>
class MyClass
{
OtherClass<p_first> first;
using splitter = Splitter<std::integer_sequence<int>, p_second, p_third, p_rest...>;
std::vector<int> middle = splitter::get_vector();
typename splitter::last_t last;
};
Demo
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.
I tried to implement the C++14 alias template make_integer_sequence, which simplifies the creation of the class template integer_sequence.
template< class T, T... I> struct integer_sequence
{
typedef T value_type;
static constexpr size_t size() noexcept { return sizeof...(I) ; }
};
template< class T, T N>
using make_integer_sequence = integer_sequence< T, 0,1,2, ... ,N-1 >; // only for illustration.
To implement make_integer_sequence we need a helper structure make_helper.
template< class T , class N >
using make_integer_sequence = typename make_helper<T,N>::type;
Implementing make_helper isn't too difficult.
template< class T, T N, T... I >
struct make_helper
{
typedef typename mpl::if_< T(0) == N,
mpl::identity< integer_sequence<T,I...> >,
make_helper< T, N-1, N-1,I...>
>::type;
};
To test make_integer_sequence I made this main function:
int main()
{
#define GEN(z,n,temp) \
typedef make_integer_sequence< int, n > BOOST_PP_CAT(int_seq,n) ;
BOOST_PP_REPEAT(256, GEN, ~);
}
I compiled the program with GCC 4.8.0, on a quad-core i5 system with 8GBs of RAM.
Successful compilation took 4 seconds.
But, when I changed the GEN macro to:
int main() {
#define GEN(z,n,temp) \
typedef make_integer_sequence< int, n * 4 > BOOST_PP_CAT(int_seq, n) ;
BOOST_PP_REPEAT(256, GEN, ~ );
}
The compilation was unsuccessful and outputted the error message:
virtual memory exhausted.
Could somebody explain this error and what caused it?
EDIT:
I simplified the test to:
int main()
{
typedef make_integer_sequence< int, 4096 > int_seq4096;
}
I then successfully compiled with GCC 4.8.0 -ftemplate-depth=65536.
However this second test:
int main()
{
typedef make_integer_sequence< int, 16384 > int_seq16384;
}
Did not compile with GCC 4.8.0 -ftemplate-depth=65536, and resulted in the error:
virtual memory exhausted.
So, my question is, how do I decrease template deep instantiation?
Regards,
Khurshid.
Here's a log N implementation that doesn't even need an increased max-depth for template instantiations and compiles pretty fast:
// using aliases for cleaner syntax
template<class T> using Invoke = typename T::type;
template<unsigned...> struct seq{ using type = seq; };
template<class S1, class S2> struct concat;
template<unsigned... I1, unsigned... I2>
struct concat<seq<I1...>, seq<I2...>>
: seq<I1..., (sizeof...(I1)+I2)...>{};
template<class S1, class S2>
using Concat = Invoke<concat<S1, S2>>;
template<unsigned N> struct gen_seq;
template<unsigned N> using GenSeq = Invoke<gen_seq<N>>;
template<unsigned N>
struct gen_seq : Concat<GenSeq<N/2>, GenSeq<N - N/2>>{};
template<> struct gen_seq<0> : seq<>{};
template<> struct gen_seq<1> : seq<0>{};
This is basically me hacking around Xeo's solution: Making community wiki - if appreciative, please upvote Xeo.
...just modified until I felt it couldn't get any simpler, renamed and added value_type and size() per the Standard (but only doing index_sequence not integer_sequence), and code working with GCC 5.2 -std=c++14 could run otherwise unaltered under older/other compilers I'm stuck with. Might save someone some time / confusion.
// based on http://stackoverflow.com/a/17426611/410767 by Xeo
namespace std // WARNING: at own risk, otherwise use own namespace
{
template <size_t... Ints>
struct index_sequence
{
using type = index_sequence;
using value_type = size_t;
static constexpr std::size_t size() noexcept { return sizeof...(Ints); }
};
// --------------------------------------------------------------
template <class Sequence1, class Sequence2>
struct _merge_and_renumber;
template <size_t... I1, size_t... I2>
struct _merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
: index_sequence<I1..., (sizeof...(I1)+I2)...>
{ };
// --------------------------------------------------------------
template <size_t N>
struct make_index_sequence
: _merge_and_renumber<typename make_index_sequence<N/2>::type,
typename make_index_sequence<N - N/2>::type>
{ };
template<> struct make_index_sequence<0> : index_sequence<> { };
template<> struct make_index_sequence<1> : index_sequence<0> { };
}
Notes:
the "magic" of Xeo's solution is in the declaration of _merge_and_renumber (concat in his code) with exactly two parameters, while the specilisation effectively exposes their individual parameter packs
the typename...::type in...
struct make_index_sequence
: _merge_and_renumber<typename make_index_sequence<N/2>::type,
typename make_index_sequence<N - N/2>::type>
avoids the error:
invalid use of incomplete type 'struct std::_merge_and_renumber<std::make_index_sequence<1ul>, std::index_sequence<0ul> >'
I found very fast and needless deep recursion version of implementation of make_index_sequence. In my PC it compiles with N = 1 048 576 , with 2 s.
(PC : Centos 6.4 x86, i5, 8 Gb RAM, gcc-4.4.7 -std=c++0x -O2 -Wall).
#include <cstddef> // for std::size_t
template< std::size_t ... i >
struct index_sequence
{
typedef std::size_t value_type;
typedef index_sequence<i...> type;
// gcc-4.4.7 doesn't support `constexpr` and `noexcept`.
static /*constexpr*/ std::size_t size() /*noexcept*/
{
return sizeof ... (i);
}
};
// this structure doubles index_sequence elements.
// s- is number of template arguments in IS.
template< std::size_t s, typename IS >
struct doubled_index_sequence;
template< std::size_t s, std::size_t ... i >
struct doubled_index_sequence< s, index_sequence<i... > >
{
typedef index_sequence<i..., (s + i)... > type;
};
// this structure incremented by one index_sequence, iff NEED-is true,
// otherwise returns IS
template< bool NEED, typename IS >
struct inc_index_sequence;
template< typename IS >
struct inc_index_sequence<false,IS>{ typedef IS type; };
template< std::size_t ... i >
struct inc_index_sequence< true, index_sequence<i...> >
{
typedef index_sequence<i..., sizeof...(i)> type;
};
// helper structure for make_index_sequence.
template< std::size_t N >
struct make_index_sequence_impl :
inc_index_sequence< (N % 2 != 0),
typename doubled_index_sequence< N / 2,
typename make_index_sequence_impl< N / 2> ::type
>::type
>
{};
// helper structure needs specialization only with 0 element.
template<>struct make_index_sequence_impl<0>{ typedef index_sequence<> type; };
// OUR make_index_sequence, gcc-4.4.7 doesn't support `using`,
// so we use struct instead of it.
template< std::size_t N >
struct make_index_sequence : make_index_sequence_impl<N>::type {};
//index_sequence_for any variadic templates
template< typename ... T >
struct index_sequence_for : make_index_sequence< sizeof...(T) >{};
// test
typedef make_index_sequence< 1024 * 1024 >::type a_big_index_sequence;
int main(){}
You are missing a -1 here:
typedef typename mpl::if_< T(0) == N,
mpl::identity< integer_sequence<T> >,
make_helper< T, N, N-1,I...>
>::type;
in particular:
typedef typename mpl::if_< T(0) == N,
mpl::identity< integer_sequence<T> >,
make_helper< T, N-1, N-1,I...>
>::type;
Next, the first branch shouldn't be integer_sequence<T>, but rather integer_sequence<T, I...>.
typedef typename mpl::if_< T(0) == N,
mpl::identity< integer_sequence<T, I...> >,
make_helper< T, N-1, N-1,I...>
>::type;
which should be enough to get your original code to compile.
In general, when writing serious template metaprogramming, your main goal should be to keep the depth of template instantiation down. A way to think about this problem is imagining you have an infinite-thread computer: each independent calculation should be shuffled off onto its own thread, then shuffled together at the end. You have a few operations that take O(1) depth, like ... expansion: exploit them.
Usually, pulling of logarithmic depth is enough, because with a 900 depth, that allows 2^900 sized structures, and something else breaks first. (To be fair, what is more likely to happen is 90 different layers of 2^10 sized structures).
Here is another slightly more general variation of Xeo's logarithmic answer which provides make_integer_sequence for arbitrary types. This is done by using std::integral_constant in order to avoid the dreaded "template argument involves template parameter" error.
template<typename Int, Int... Ints>
struct integer_sequence
{
using value_type = Int;
static constexpr std::size_t size() noexcept
{
return sizeof...(Ints);
}
};
template<std::size_t... Indices>
using index_sequence = integer_sequence<std::size_t, Indices...>;
namespace
{
// Merge two integer sequences, adding an offset to the right-hand side.
template<typename Offset, typename Lhs, typename Rhs>
struct merge;
template<typename Int, Int Offset, Int... Lhs, Int... Rhs>
struct merge<
std::integral_constant<Int, Offset>,
integer_sequence<Int, Lhs...>,
integer_sequence<Int, Rhs...>
>
{
using type = integer_sequence<Int, Lhs..., (Offset + Rhs)...>;
};
template<typename Int, typename N>
struct log_make_sequence
{
using L = std::integral_constant<Int, N::value / 2>;
using R = std::integral_constant<Int, N::value - L::value>;
using type = typename merge<
L,
typename log_make_sequence<Int, L>::type,
typename log_make_sequence<Int, R>::type
>::type;
};
// An empty sequence.
template<typename Int>
struct log_make_sequence<Int, std::integral_constant<Int, 0>>
{
using type = integer_sequence<Int>;
};
// A single-element sequence.
template<typename Int>
struct log_make_sequence<Int, std::integral_constant<Int, 1>>
{
using type = integer_sequence<Int, 0>;
};
}
template<typename Int, Int N>
using make_integer_sequence =
typename log_make_sequence<
Int, std::integral_constant<Int, N>
>::type;
template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
Demo: coliru
Simple implementation O(N). Probably not what you want for large N, but my application is only for calling functions with indexed arguments, and I wouldn't expect an arity of more than about 10. I haven't filled in the members of integer_sequence. I'm looking forward to using a standard library implementation and nuking this :)
template <typename T, T... ints>
struct integer_sequence
{ };
template <typename T, T N, typename = void>
struct make_integer_sequence_impl
{
template <typename>
struct tmp;
template <T... Prev>
struct tmp<integer_sequence<T, Prev...>>
{
using type = integer_sequence<T, Prev..., N-1>;
};
using type = typename tmp<typename make_integer_sequence_impl<T, N-1>::type>::type;
};
template <typename T, T N>
struct make_integer_sequence_impl<T, N, typename std::enable_if<N==0>::type>
{ using type = integer_sequence<T>; };
template <typename T, T N>
using make_integer_sequence = typename make_integer_sequence_impl<T, N>::type;
Here is another implementation technique (for T=size_t), it uses C++17 fold expressions and bitwise generation (i.e. O(log(N)):
template <size_t... Is>
struct idx_seq {
template <size_t N, size_t Offset>
struct pow2_impl {
using type = typename idx_seq<Is..., (Offset + Is)...>::template pow2_impl<N - 1, Offset + sizeof...(Is)>::type;
};
template <size_t _> struct pow2_impl<0, _> { using type = idx_seq; };
template <size_t _> struct pow2_impl<(size_t)-1, _> { using type = idx_seq<>; };
template <size_t Offset>
using offset = idx_seq<(Offset + Is)...>;
};
template <size_t N>
using idx_seq_pow2 = typename idx_seq<0>::template pow2_impl<N, 1>::type;
template <size_t... Is, size_t... Js>
constexpr static auto operator,(idx_seq<Is...>, idx_seq<Js...>)
-> idx_seq<Is..., Js...>
{ return {}; }
template <size_t N, size_t Mask, size_t... Bits>
struct make_idx_seq_impl {
using type = typename make_idx_seq_impl<N, (N >= Mask ? Mask << 1 : 0), Bits..., (N & Mask)>::type;
};
template <size_t N, size_t... Bits>
struct make_idx_seq_impl<N, 0, Bits...> {
using type = decltype((idx_seq<>{}, ..., typename idx_seq_pow2<Bits>::template offset<(N & (Bits - 1))>{}));
};
template <size_t N>
using make_idx_seq = typename make_idx_seq_impl<N, 1>::type;
Here is a very simple solution implemented with recursion based on tag dispatching
template <typename T, T M, T ... Indx>
constexpr std::integer_sequence<T, Indx...> make_index_sequence_(std::false_type)
{
return {};
}
template <typename T, T M, T ... Indx>
constexpr auto make_index_sequence_(std::true_type)
{
return make_index_sequence_<T, M, Indx..., sizeof...(Indx)>(
std::integral_constant<bool, sizeof...(Indx) + 1 < M>());
}
template <size_t M>
constexpr auto make_index_sequence()
{
return make_index_sequence_<size_t, M>(std::integral_constant<bool, (0 < M)>());
}
However, this solution can not be extended to C++11.