Trouble with syntax for template-template-templates - c++

I'm writing a meta-function MultipartitionWithUnaryPredicates, used in the form
MultipartitionWithUnaryPredicates<Pack<Args...>, UnaryPredicates...>::type
so that a template pack of types will be multipartitioned by a pack UnaryPredicates... of unary predicates, with the partitions in the order of the unary predicates listed. If you're not sure what I mean, just check out main() in the code below (which works correctly):
#include <iostream>
#include <type_traits>
#include <typeInfo>
template <template <typename> class, typename, typename, typename> struct Helper;
template <template <typename> class UnaryPredicate, template <typename...> class P, typename... Types1, typename... Types2>
struct Helper<UnaryPredicate, P<>, P<Types1...>, P<Types2...>> {
using type = P<Types1..., Types2...>;
using head = P<Types1...>;
using tail = P<Types2...>;
};
template <template <typename> class UnaryPredicate, template <typename...> class P, typename First, typename... Rest, typename... Types1, typename... Types2>
struct Helper<UnaryPredicate, P<First, Rest...>, P<Types1...>, P<Types2...>> : std::conditional<UnaryPredicate<First>::value,
Helper<UnaryPredicate, P<Rest...>, P<Types1..., First>, P<Types2...>>,
Helper<UnaryPredicate, P<Rest...>, P<Types1...>, P<Types2..., First>>
>::type {};
template <typename, template <typename> class> struct PartitionWithUnaryPredicate;
template <template <typename> class UnaryPredicate, template <typename...> class P, typename... Ts>
struct PartitionWithUnaryPredicate<P<Ts...>, UnaryPredicate> : Helper<UnaryPredicate, P<Ts...>, P<>, P<>> {};
template <typename, template <typename> class...> struct MultipartitionWithUnaryPredicates;
template <typename Pack, template <typename> class UnaryPredicate>
struct MultipartitionWithUnaryPredicates<Pack, UnaryPredicate> : PartitionWithUnaryPredicate<Pack, UnaryPredicate> {};
template <typename, typename> struct Join;
template <template <typename...> class P, typename... Types1, typename... Types2>
struct Join<P<Types1...>, P<Types2...>> {
using type = P<Types1..., Types2...>;
};
//template <template <typename, template <typename> class> class Pack, typename... Ts>
//struct JoinSpecial : Join<typename Pack::head, typename MultipartitionWithUnaryPredicates<typename Pack::tail, Ts...>::type> {};
template <typename Pack, template <typename> class First, template <typename> class... Rest>
struct MultipartitionWithUnaryPredicates<Pack, First, Rest...> : Join<typename PartitionWithUnaryPredicate<Pack, First>::head, typename MultipartitionWithUnaryPredicates<typename PartitionWithUnaryPredicate<Pack, First>::tail, Rest...>::type> {};
// The above can be improved, since PartitionWithUnaryPredicate<Pack, First> is being computed twice.
// -----------------------------------------------------------------------------------------------------------------------------------------------
// Testing:
template <typename...> struct Pack {};
template <typename Last>
struct Pack<Last> {
static void print() {std::cout << typeid(Last).name() << std::endl;}
};
template <typename First, typename ... Rest>
struct Pack<First, Rest...> {
static void print() {std::cout << typeid(First).name() << ' '; Pack<Rest...>::print();}
};
struct Thing {};
struct Blob { Blob(Blob&&){} }; // Copy constructor deleted.
struct Object {};
enum MyEnum {x, y, z};
enum HerEnum {xx, yy, zz};
int main() {
MultipartitionWithUnaryPredicates<Pack<int, Thing, double, short, Blob, char, MyEnum, long, Object, float, HerEnum>,
std::is_integral>::type b;
b.print(); // int short char long Thing double Blob MyEnum Object float HerEnum
MultipartitionWithUnaryPredicates<Pack<int, Thing, double, short, Blob, char, MyEnum, long, Object, float, HerEnum>,
std::is_integral, std::is_enum>::type c;
c.print(); // int short char long MyEnum HerEnum Thing double Blob Object float
MultipartitionWithUnaryPredicates<Pack<int, Thing, double, short, Blob, char, MyEnum, long, Object, float, HerEnum>,
std::is_integral, std::is_enum, std::is_arithmetic>::type d;
d.print(); // int short char long MyEnum HerEnum double float Thing Blob Object
MultipartitionWithUnaryPredicates<Pack<int, Thing, double, short, Blob, char, MyEnum, long, Object, float, HerEnum>,
std::is_integral, std::is_enum, std::is_arithmetic, std::is_member_pointer, std::is_copy_constructible>::type e;
e.print(); // int short char long MyEnum HerEnum double float Thing Object Blob
}
The problem is trying to refine the above code.
PartitionWithUnaryPredicate<Pack, First>
is being computed twice, I'm trying to define JoinSpecial that will use it just once. But I can't get the syntax correct.
template <template <typename, template <typename> class> class Pack, typename... Ts>
struct JoinSpecial : Join<typename Pack::head, typename MultipartitionWithUnaryPredicates<typename Pack::tail, Ts...>::type> {};
does not compile. The template type PartitionWithUnaryPredicate is of type template <typename, template <typename> class> class is it not?
Update:
Thanks to Angew's tip, I got the syntax correct now:
template <template <typename, template <typename> class> class P, typename Pack, template <typename> class Pred, template <typename> class... Ts>
struct JoinSpecial : Join<typename P<Pack, Pred>::head, typename MultipartitionWithUnaryPredicates<typename P<Pack, Pred>::tail, Ts...>::type> {};
template <typename Pack, template <typename> class First, template <typename> class... Rest>
struct MultipartitionWithUnaryPredicates<Pack, First, Rest...> : JoinSpecial<PartitionWithUnaryPredicate, Pack, First, Rest...> {};
Now everything works correctly--my first time using template-template-templates, which I must say looks quite ugly.

In your definition of JoinSpecial, Pack is a class template. Then, in specifying template arguments to Join, you have typename Pack::head and typename Pack::tail. But Pack is a template, not a class — you need to supply template arguments for Pack.

Related

Expanding with pack of templates

Define template_pack as in the following example. Given
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
then
template_pack<std::tuple<char, bool, double>, A, B, C>::type
is to be
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
i.e. always reading from left to right in the tuple, so as to get enough types to fit each template.
Here's my solution so far. But it only works for templates that take up to 3 types, and I don't see how I can generalize to templates of any number of types:
#include <type_traits>
#include <tuple>
template <template <typename...> class Template, typename... Ts> struct use_template;
template <template <typename> class Template, typename A, typename... Rest>
struct use_template<Template, A, Rest...> {
using type = Template<A>;
};
template <template <typename, typename> class Template, typename A, typename B, typename... Rest>
struct use_template<Template, A, B, Rest...> {
using type = Template<A,B>;
};
template <template <typename, typename, typename> class Template, typename A, typename B, typename C, typename... Rest>
struct use_template<Template, A, B, C, Rest...> {
using type = Template<A,B,C>;
};
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts, template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<typename use_template<Templates, Ts...>::type...>;
};
// Testing
template <typename> struct A;
template <typename, typename, typename> struct B;
template <typename, typename> struct C;
int main() {
static_assert (std::is_same<
template_pack<std::tuple<char, bool, double>, A, B, C>::type,
std::tuple<A<char>, B<char, bool, double>, C<char, bool>>
>::value, "");
}
How to generalize the above? Is it even possible?
This is "find shortest prefix".
Utilities:
// pack that is easy to append to
template<class... Ts>
struct pack{
template<class... T1s>
using append = pack<Ts..., T1s...>;
};
template<class...> using void_t = void;
The meat:
// find the shortest proper prefix Ts... of [T, Rest...]
// such that A<Ts...> is well-formed, and return it.
template<template<class...> class A, // template we are going to look at
class AlwaysVoid, // for void_t
class Current, // pack containing the types we are testing next
class T, // next type to add to pack if test fails
class... Rest> // remaining types
struct find_viable
: find_viable<A, AlwaysVoid, typename Current::template append<T>, Rest...> {};
// picked if A<Ts...> is well-formed
template<template<class...> class A, class... Ts, class T, class...Rest>
struct find_viable<A, void_t<A<Ts...>>, pack<Ts...>, T, Rest...> {
using type = A<Ts...>;
};
// Finds the shortest prefix of Ts... such that A<prefix...> is well formed.
// the extra void at the end is slightly hackish - find_viable only checks
// proper prefixes, so we add an extra dummy type at the end.
template<template<class...> class A, class... Ts>
using find_viable_t = typename find_viable<A, void, pack<>, Ts..., void>::type;
Then use it:
template <typename Pack, template <typename...> class... Templates> struct template_pack;
template <template <typename...> class P, typename... Ts,
template <typename...> class... Templates>
struct template_pack<P<Ts...>, Templates...> {
using type = P<find_viable_t<Templates, Ts...>...>;
};
I won't write it all, but I'll do a walkthrough.
template<template<class...>class Z>
struct z_template{
template<class...Ts>using result=Z<Ts...>;
};
this lets you manipulate templates like types.
template<template<class...>class Z, class...Ts>
using can_apply = /* ... */;
this asks the question "is Z<Ts...> valid? If so, return true_type".
template<class...>struct types{using type=types;};
this is a light-weight template pack.
template<template<class...>class Z, class types>
using apply = /* ... */;
this takes a template Z, and a types<Ts...>, and returns Z<Ts...>.
template<class Z, class types>
using z_apply = apply<typename Z::template result, types>;
this does it with a z_template argument instead of a template one.
Working on types as often as possible makes many kinds of metaprogramming easier.
Next we write
template<class types>using pop_front = // ...
template<class types>using reverse = // ...
template<class types>using pop_back = reverse<pop_front<reverse<types>>>;
which remove elements from the front and back of types.
template<class Result>
struct z_constant {
template<class...Ts>using result=Result;
};
This finds the longest prefix of types that can be passed to Z::template result<???> validly. Some work to avoid passing too-short sequences is done, in case they fail "late":
template<class Z, class types>
struct z_find_longest_prefix :
std::conditional_t<
can_apply< z_apply, Z, types >{},
z_constant<types>,
z_template<longest_prefix>
>::template result<Z, pop_back<types>>
{};
template<class Z, class types>
using z_find_longest_prefix_t = typename z_find_longest_prefix<Z,types>::type;
struct empty_t {};
template<class Z>
struct z_find_longest_prefix<Z,types<>>:
std::conditional_t<
can_apply< Z::template result >,
types<>,
empty_t
>
{};
find_longest_prefix will fail to compile if there is no valid prefix.
template<class Z, class types>
using z_apply_longest_prefix_t =
z_apply< Z, z_find_longest_prefix_t<Z, types> >;
template<class src, template<class...>class... Zs>
struct use_template;
template<class src, template<class...>class... Zs>
using use_template_t = typename use_template<src, Zs...>::type;
template<template<class...>class Z0, class...Ts, template<class...>class... Zs>
struct use_template< Z0<Ts...>, Zs... > {
using type=Z0<
z_apply_longest_prefix_t<
z_template<Zs>, types<Ts...>
>...
>;
};
and up to typos and similar problems, done.
Can be cleaned up lots. Probably the entire z_ subsystem isn't needed. I just tend to run towards it whenever doing serious metaprogramming, because suddenly any metafunction I write that applies to types now can be used on templates.
reverse takes a bit of work to do efficiently.
can_apply is available in many of my posts.
pop_front is trivial.
apply should be easy.
This looked fun so I tried my hand at it; I'm not claiming this is in any way better than the existing answers, but maybe some people will find it clearer:
namespace detail {
template<typename...>
using void_t = void;
template<template<typename...> class, typename, typename, typename EnableT = void>
struct fill_template;
template<template<typename...> class TemplateT, typename TupleT, std::size_t... Is>
struct fill_template<
TemplateT, TupleT, std::index_sequence<Is...>,
void_t<TemplateT<std::tuple_element_t<Is, TupleT>...>>
>
{
using type = TemplateT<std::tuple_element_t<Is, TupleT>...>;
};
template<
template<typename...> class TemplateT, typename TupleT,
std::size_t I, std::size_t N
>
struct fill_template_dispatcher
{
template<typename T, typename = typename T::type>
static constexpr std::true_type test(int) { return {}; }
template<typename T>
static constexpr std::false_type test(long) { return {}; }
using candidate_t = fill_template<TemplateT, TupleT, std::make_index_sequence<I>>;
using type = typename std::conditional_t<
test<candidate_t>(0),
candidate_t,
fill_template_dispatcher<TemplateT, TupleT, I + 1, I != N ? N : 0>
>::type;
};
template<template<typename...> class TemplateT, typename TupleT, std::size_t I>
struct fill_template_dispatcher<TemplateT, TupleT, I, 0>
{
static_assert(I != I, "tuple contains insufficient types to satisfy all templates");
};
} // namespace detail
template<typename TupleT, template<typename...> class... TemplateTs>
struct template_pack
{
static_assert(std::tuple_size<TupleT>::value, "tuple must not be empty");
using type = std::tuple<typename detail::fill_template_dispatcher<
TemplateTs, TupleT, 0, std::tuple_size<TupleT>::value
>::type...>;
};
template<typename TupleT, template<typename...> class... TemplateTs>
using template_pack_t = typename template_pack<TupleT, TemplateTs...>::type;
Online Demo
Note that for variadic class templates this will pass as few types as possible (see D in the test); for an implementation that passes as many types as possible instead, see that variation of the code here (only two lines are changed in the implementation).

Generating a template pack

Generate<P<3>, P<5,0>, P<4,0,0>, P<3,0,1>>::type is to be
Pack< A<0>, A<0,0>, A<0,0,0>, A<0,0,1>, A<0,0,2>, A<0,0,3>, A<0,1>, A<0,1,0>, A<0,1,1>, A<0,1,2>, A<0,2>, A<0,3>, A<0,4>, A<1>, A<2> >
because P<3> means that P<n> exists for n = 0,1,2; P<5,0> means that A<0,n> exists for n = 0,1,2,3,4; P<4,0,0> means that A<0,0,n> exists for n = 0,1,2,3; and P<3,0,1> means that A<0,1,n> exists for n = 0,1,2.
In terms of ordering, A<n1,n2,n3,...,nk,x,...> will always precede A<n1,n2,n3,...,nk,y,...> if x < y and A<n1,n2,n3,...,nk> will always precede A<n1,n2,n3,...,nk, ...>, where the second elipses ... is non-empty.
So I need to write out the implementation of Generate<Packs...>.
Motivation for this: If template <int... Is> class Object has certain possibilities for its Is... pack, defined by constants like the 3, 5, 4, and 3 above, then a pack of all of its possible types will allow
generation of specific Object<Is...> instances by iterating through the pack.
Here is what I have so far:
#include <iostream>
#include <type_traits>
template <int...> class A;
template <typename...> struct Pack;
template <int...> struct P;
template <int N, int Count, typename Front, typename Output> struct GenerateHelper;
template <int N, int Count, int... Is, typename... As>
struct GenerateHelper<N, Count, P<Is...>, Pack<As...>> :
GenerateHelper<N, Count + 1, P<Is...>, Pack<As..., A<Is..., Count>>> {};
template <int N, int... Is, typename... As>
struct GenerateHelper<N, N, P<Is...>, Pack<As...>> {
using type = Pack<As...>;
};
template <typename...> struct Generate;
// Simple special case just to start off. Generate has only one pack to deal with.
template <int N, int... Is>
struct Generate<P<N, Is...>> : GenerateHelper<N, 0, P<Is...>, Pack<>> {};
int main() {
using T = Generate<P<3,0,0>>::type;
std::cout << std::is_same<T, Pack<A<0,0,0>, A<0,0,1>, A<0,0,2>>>::value << '\n'; // true
}
But now I'm stuck with the case of just 2 packs in Generate. Can anyone help me continue?
Idea: For 2 packs, just generate separately, merge the two Packs, and then sort after? But then the sorting will be the hardest part I think.
The trick is realizing that the sequence you get from each "generation" procedure is already sorted, and the problem reduces to merging several sorted lists.
For simplicity, I made A, Pack and P empty structs.
template <int...> class A {};
template <typename...> struct Pack {};
template <int...> struct P {};
Generate a pack of As from one P:
template<int I, int... Tail>
auto do_sequence_for(P<I, Tail...>) -> std::make_integer_sequence<int, I>;
template<class PP>
using sequence_for = decltype(do_sequence_for(PP()));
template<int I, int... Front, int... Tail>
auto do_generate_single(P<I, Front...>, std::integer_sequence<int, Tail...>)
-> Pack<A<Front..., Tail>...>;
template<class PP>
using generate_single = decltype(do_generate_single(PP(), sequence_for<PP>()));
Lexicographical comparison of two As:
template<class A1, class A2>
struct compare; // returns A1 < A2
template<int I, int J, int...Is, int...Js>
struct compare<A<I, Is...>, A<J, Js...>> : std::integral_constant<bool, I < J> {};
template<int I, int...Is, int...Js>
struct compare<A<I, Is...>, A<I, Js...>> : compare<A<Is...>, A<Js...>> {};
template<int...Is>
struct compare<A<Is...>, A<>> : std::false_type {};
template<int J, int...Js>
struct compare<A<>, A<J, Js...>> : std::true_type {};
Merging two sorted packs of As:
template<class Pack1, class Pack2, class Result=Pack<>>
struct merge2;
template<class A1, class...A1s, class A2, class...A2s, class...R>
struct merge2<Pack<A1, A1s...>, Pack<A2, A2s...>, Pack<R...>>
: std::conditional_t<compare<A1, A2>::value,
merge2<Pack<A1s...>, Pack<A2, A2s...>, Pack<R..., A1>>,
merge2<Pack<A1, A1s...>, Pack<A2s...>, Pack<R..., A2>>>
{};
template<class...A1s, class...R>
struct merge2<Pack<A1s...>, Pack<>, Pack<R...>>
{
using type = Pack<R..., A1s...>;
};
template<class A2, class...A2s, class...R>
struct merge2<Pack<>, Pack<A2, A2s...>, Pack<R...>>
{
using type = Pack<R..., A2, A2s...>;
};
Merge many sorted packs of As:
template<class... Packs>
struct merge;
template<class P1>
struct merge<P1> {
using type = P1;
};
template<class P1, class P2, class... Ps>
struct merge<P1, P2, Ps...> : merge<typename merge2<P1, P2>::type, Ps...> {};
Bringing it all together:
template<class...Ps>
struct Generate{
using type = typename merge<generate_single<Ps>...>::type;
};
Demo.
Unfortunately, Visual Studio 2015 will not compile T.C.'s excellent solution (due to some bug it has), so the following is adapted by me that compiles on both Visual Studio 2015 and GCC 5.1.0 (and also generalizes to any integral type, any class, etc...). Only his generate_single function needed to be replaced.
#include <iostream>
#include <utility>
#include <type_traits>
// Another way to generate a pack of A's from one P than the above.
template <typename T, template<T...> class Class, T N, T Count, typename Front, typename Output> struct GenerateSingleHelper;
template <typename T, template<T...> class Class, T N, T Count, template <T...> class Z, T... Is, template <typename...> class PackOfPacks, typename... As>
struct GenerateSingleHelper<T, Class, N, Count, Z<Is...>, PackOfPacks<As...>> : GenerateSingleHelper<T, Class, N, Count + 1, Z<Is...>, PackOfPacks<As..., Class<Is..., Count>>> {};
template <typename T, template<T...> class Class, T N, template <T...> class Z, T... Is, template <typename...> class PackOfPacks, typename... As>
struct GenerateSingleHelper<T, Class, N, N, Z<Is...>, PackOfPacks<As...>> {
using type = PackOfPacks<As...>;
};
template <typename T, template<T...> class Class, template <typename...> class PackOfPacks, typename> struct GenerateSingle;
template <typename T, template<T...> class Class, template <typename...> class PackOfPacks, template <T...> class Z, T N, T... Is>
struct GenerateSingle<T, Class, PackOfPacks, Z<N, Is...>> : GenerateSingleHelper<T, Class, N, 0, Z<Is...>, PackOfPacks<>> {};
// Lexicographical comparison of two A's.
template <typename T, typename A1, typename A2> struct Compare; // Determines if A1 < A2.
template <typename T, template <T...> class Pack, T I, T J, T... Is, T... Js>
struct Compare<T, Pack<I, Is...>, Pack<J, Js...>> : std::integral_constant<bool, I < J> {};
template <typename T, template <T...> class Pack, T I, T... Is, T... Js>
struct Compare<T, Pack<I, Is...>, Pack<I, Js...>> : Compare<T, Pack<Is...>, Pack<Js...>> {};
template <typename T, template <T...> class Pack, T... Is>
struct Compare<T, Pack<Is...>, Pack<>> : std::false_type {};
template <typename T, template <T...> class Pack, T J, T... Js>
struct Compare<T, Pack<>, Pack<J, Js...>> : std::true_type {}; // J is needed to indicate that Pack<J, Js...> is not empty.
// Merging two sorted packs of A's.
template <typename T, template <typename...> class PackOfPacks, typename PackOfPacks1, typename PackOfPacks2, typename Result = PackOfPacks<>> struct MergeTwoPacks;
template <typename T, template <typename...> class PackOfPacks, typename A1, typename... A1s, typename A2, typename... A2s, typename... Accumulated>
struct MergeTwoPacks<T, PackOfPacks, PackOfPacks<A1, A1s...>, PackOfPacks<A2, A2s...>, PackOfPacks<Accumulated...>> : std::conditional_t<Compare<T, A1, A2>::value,
MergeTwoPacks<T, PackOfPacks, PackOfPacks<A1s...>, PackOfPacks<A2, A2s...>, PackOfPacks<Accumulated..., A1>>,
MergeTwoPacks<T, PackOfPacks, PackOfPacks<A1, A1s...>, PackOfPacks<A2s...>, PackOfPacks<Accumulated..., A2>>> {};
template <typename T, template <typename...> class PackOfPacks, typename... A1s, typename... Accumulated>
struct MergeTwoPacks<T, PackOfPacks, PackOfPacks<A1s...>, PackOfPacks<>, PackOfPacks<Accumulated...>> {
using type = PackOfPacks<Accumulated..., A1s...>; // Since PackOfPacks<A1s...> is already sorted.
};
template <typename T, template <typename...> class PackOfPacks, typename A2, typename... A2s, typename... Accumulated>
struct MergeTwoPacks<T, PackOfPacks, PackOfPacks<>, PackOfPacks<A2, A2s...>, PackOfPacks<Accumulated...>> { // A2 is needed to indicate that PackOfPacks<A2, A2s...> is not empty.
using type = PackOfPacks<Accumulated..., A2, A2s...>; // Since PackOfPacks<A2s...> is already sorted.
};
// Merging any number of sorted packs of A's.
template <typename T, template <typename...> class PackOfPacks, typename... Packs> struct Merge;
template<typename T, template <typename...> class PackOfPacks, typename First, typename Second, typename... Rest>
struct Merge<T, PackOfPacks, First, Second, Rest...> : Merge<T, PackOfPacks, typename MergeTwoPacks<T, PackOfPacks, First, Second>::type, Rest...> {};
template <typename T, template <typename...> class PackOfPacks, typename Last>
struct Merge<T, PackOfPacks, Last> {
using type = Last;
};
// Putting it all together.
template <typename T, template <T...> class Class, template <typename...> class PackOfPacks, typename... Packs>
struct Generate : Merge<T, PackOfPacks, typename GenerateSingle<T, Class, PackOfPacks, Packs>::type...> {};
// Testing.
template <int...> class A {};
template <typename...> struct PackOfPacks;
template <int...> struct P;
int main() {
std::cout << std::boolalpha << std::is_same<
Generate<int, A, PackOfPacks, P<3>, P<5,0>, P<4,0,0>, P<3,0,1>>::type,
PackOfPacks< A<0>, A<0,0>, A<0,0,0>, A<0,0,1>, A<0,0,2>, A<0,0,3>, A<0,1>, A<0,1,0>, A<0,1,1>, A<0,1,2>, A<0,2>, A<0,3>, A<0,4>, A<1>, A<2> >
>::value << '\n'; // true
}

Indices trick used for several components

Consider this fully working code:
#include <type_traits>
template <typename T, typename IndexPack> struct Make;
template <typename T, template <T...> class P, T... Indices>
struct Make<T, P<Indices...>> {
using type = P<(Indices+1)..., (-3*Indices)..., (Indices-1)...>;
};
template <int...> class Pack;
int main() {
static_assert (std::is_same<Make<int, Pack<1,2,3,4>>::type,
Pack<2,3,4,5, -3,-6,-9,-12, 0,1,2,3>>::value, "false");
}
What I actually want the output to be is
Pack<2,-3,0, 3,-6,1, 4,-9,2, 5,-12,3>
instead of Pack<2,3,4,5, -3,-6,-9,-12, 0,1,2,3>. I first tried
using type = P<(Indices+1, -3*Indices, Indices-1)...>;
but that is simply understood by the compiler to be a useless comma operator. What is the desired syntax to get what I want? If there is no such syntax, what is the cleanest way to do this, keeping in mind that using Indices 3 times is just an example (we may want to use it more than 3 times). Please don't tell me that I have to write a helper to extract the individual packs and then "interlace" all the elements. That nightmarish method cannot be the best solution (and such a solution would also only work if we knew exactly how many individual packs to extract).
Would defining
template <typename T, template <T...> class P, T I>
struct Component {
using type = P<I+1, -3*I, I-1>;
};
help somehow? Make a pack expansion on this?
Yes, you can concat recursively:
template <typename, typename, typename> struct Concat;
template <typename T, template <T...> class P, T... A, T... B>
struct Concat<T, P<A...>, P<B...>> {
using type = P<A..., B...>;
};
template <typename T, typename IndexPack> struct Make;
template <typename T, template <T...> class P, T... I, T F >
struct Make<T, P<F, I...>> {
using type = typename Concat<T,
typename Make<T, P<F>>::type,
typename Make<T, P<I...>>::type>::type;
};
template <typename T, template <T...> class P, T I>
struct Make<T, P<I>> {
using type = P<I+1, -3*I, I-1>;
};
Demo
This was inspired by Columbo's solution. It uses the pack expansion syntax that I originally sought, namely
using type = typename Merge<T, typename Component<T, P, Indices>::type...>::type;
As a result, now Make is reusable, first using Triple, and then using Quadruple, so any number of usages of Indices can be expanded simultaneously. Here Component is a template-template-template parameter passed into Make:
#include <type_traits>
template <typename T, typename... Packs> struct Merge;
template <typename T, template <T...> class P1, template <T...> class P2, T... Is, T... Js>
struct Merge<T, P1<Is...>, P2<Js...>> {
using type = P1<Is..., Js...>;
};
template <typename T, typename Pack1, typename Pack2, typename... Packs>
struct Merge<T, Pack1, Pack2, Packs...> {
using type = typename Merge<T, Pack1, typename Merge<T, Pack2, Packs...>::type>::type;
};
template <typename T, template <T...> class P, T I>
struct Triple {
using type = P<I+1, -3*I, I-1>;
};
template <typename T, template <T...> class P, T I>
struct Quadruple {
using type = P<I+1, -3*I, I-1, I>;
};
template <typename T, typename IndexPack,
template <typename U, template <U...> class P, U I> class Component> struct Make;
template <typename T, template <T...> class Z, T... Indices,
template <typename U, template <U...> class P, U I> class Component>
struct Make<T, Z<Indices...>, Component> {
using type = typename Merge<T, typename Component<T, Z, Indices>::type...>::type;
};
template <int...> class Pack;
int main() {
static_assert (std::is_same<Make<int, Pack<1,2,3,4>, Triple>::type,
Pack<2,-3,0, 3,-6,1, 4,-9,2, 5,-12,3>>::value, "false");
static_assert (std::is_same<Make<int, Pack<1,2,3,4>, Quadruple>::type,
Pack<2,-3,0,1, 3,-6,1,2, 4,-9,2,3, 5,-12,3,4>>::value, "false");
}

Changing part of a template pack using a pre-written pack transformation

Suppose you have something like
template <typename, typename, int, typename, int, typename...> struct P
and you want to reverse the typename... part only. Now you've already written the generic reverse transformation:
// Reverse<Pack<Types...>>::type is Pack<Types'...>, where Types'... is Types... reversed.
template <typename, typename> struct ReverseHelper;
template <template <typename...> class P, typename Pack>
struct ReverseHelper<P<>, Pack> {
using type = Pack;
};
template <template <typename...> class P, typename First, typename... Rest, typename... Types>
struct ReverseHelper<P<First, Rest...>, P<Types...>> : ReverseHelper<P<Rest...>, P<First, Types...>> {};
template <typename> struct Reverse;
template <template <typename...> class P, typename... Types>
struct Reverse<P<Types...>> : ReverseHelper<P<Types...>, P<>> {};
Of course, we could rewrite the above with template <typename, typename, int, typename, int, typename...> class P instead, namely:
template <typename, typename> struct ReverseHelper1;
template <template <typename, typename, int, typename, int, typename...> class P,
typename U, typename V, int M, typename W, int N, typename Pack>
struct ReverseHelper1<P<U,V,M,W,N>, Pack> {
using type = Pack;
};
template <template <typename, typename, int, typename, int, typename...> class P,
typename U, typename V, int M, typename W, int N, typename First, typename... Rest, typename... Types>
struct ReverseHelper1<P<U,V,M,W,N, First, Rest...>, P<Types...>> : ReverseHelper<P<U,V,M,W,N, Rest...>, P<First, Types...>> {};
template <typename> struct Reverse1;
template <template <typename, typename, int, typename, int, typename...> class P,
typename U, typename V, int M, typename W, int N, typename... Types>
struct Reverse1<P<U,V,M,W,N, Types...>> : ReverseHelper1<P<U,V,M,W,N, Types...>, P<U,V,M,W,N>> {};
Notice we are just repeating? And then we would have to do this again and again for other template signatures if we want to do the same partial reversal thing. So how to make the approach by using the original Reverse itself to avoid all this repetition?
For example, let's suppose we have
template <typename> struct Foo;
template <typename> struct Bar;
template <template <typename, typename, int, typename, int, typename...> class P,
typename U, typename V, int M, typename W, int N, typename... Args>
struct Foo<P<U,V,M,W,N, Args...>> {};
Let's have Foo<P<U,V,M,W,N, Args...>> derive from Bar<P<U,V,M,W,N, ArgsReversed...>>. How to accomplish this using the defined Reverse from above?
Note, it's not the same thing as
template <template <typename, typename, int, typename, int, typename> class P,
typename U, typename V, int M, typename W, int N,
template <typename...> class Q, typename... Args>
struct Foo<P<U,V,M,W,N, Q<Args...>>> : Bar<P<U,V,M,W,N, typename Reverse<Q<Args...>>::type>> {};
though I suspect accomplishing it is done something along the lines of this. Of course, reversing is just an example. We want to reuse any transformation to make the same transformation for only part of (any) larger template structure.
The easy way is to stop using int as arguments to templates.
Barring that, you can write metafunctions for a particular pattern of class and int types, and "lift" both the template and the instance to being about classes (where int is replaced with integral_constant), and operate on those (write "reverse types after N types" to get the reverse done).
Barring that, we can hand code for your particular pattern.
template<class...>struct types{using type=types;};
namespace details {
template<class T, class types>
struct replace_tail;
template<class T, class types>
using replace_tail_t = typename replace_tail<T,types>::type;
template<class T>
struct get_tail;
template<class T>
using get_tail_t = typename get_tail<T,types>::type;
template<template <class, class, int, class, int, class...> class P,
class A, class B, int C, class D, int E, class...Fs,
class... Ts
>
struct replace_tail<P<A,B,C,D,E,Fs...>,types<Ts...>> {
using type = P<A,B,C,D,E,Ts...>;
};
template<template <class, class, int, class, int, class...> class P,
class A, class B, int C, class D, int E, class...Fs
>
struct get_tail<P<A,B,C,D,E,Fs...>>:types<Fs...>{};
template<class T>
using reverse_t = ReverseHelper<T>::type;
template<class T>
using reverse_tail = replace_tail_t < T, reverse_t<get_tail_t<T>> >;
}
using details::reverse_tail;
which may contain syntax errors. The plan is to break it into 3 parts.
First, reversing a pack (you wrote this already).
Second, extracting the "tail" arguments to reverse from an instance into a pack.
Third, replacing the "tail" arguments with another pack.
Hooked together, we reverse the tail. As a specializing get_tail_t and replace_tail_t for a new pattern of template arguments will make reverse_tail_t "just work".
Let's make a typelist:
template <typename...> struct typelist { };
You can get the N-th type of the typelist:
template <size_t N, typename> struct typelist_get;
template <typename T, typename ...Ts>
struct typelist_get<0, typelist<T, Ts...>>
{
using type = T;
};
template <size_t N, typename T, typename ...Ts>
struct typelist_get<N, typelist<T, Ts...>>
: typelist_get<N - 1, typelist<Ts...>>
{ };
You can reverse a typelist:
template <typename, typename> struct reverse_helper;
template <typename T, typename ...Ts, typename ...Rs>
struct reverse_helper<typelist<T, Ts...>, typelist<Rs...>>
: reverse_helper<typelist<Ts...>, typelist<T, Rs...>>
{ };
template <typename ...Rs>
struct reverse_helper<typelist<>, typelist<Rs...>>
{
using type = typelist<Rs...>;
};
template <typename T> struct typelist_reverse
: reverse_helper<T, typelist<>>
{ };
We also need a index_sequence:
template <size_t...> struct index_sequence;
and a way to build an index_sequence<0, 1, ..., N - 1> for given N:
template <std::size_t N, std::size_t ...I>
struct index_sequence_builder
{
using type = typename index_sequence_builder<N - 1, N - 1, I...>::type;
};
template <std::size_t ... I>
struct index_sequence_builder<0, I...>
{
using type = index_sequence<I...>;
};
template <std::size_t N>
using make_index_sequence = typename index_sequence_builder<N>::type;
Let's say we have some variadic template class Foo:
template <typename ...Ts> struct Foo { };
Then we can reverse it as follows:
template <typename, typename> struct reverse_foo_impl;
template <typename ...Ts, size_t ...I>
struct reverse_foo_impl<Foo<Ts...>, index_sequence<I...>>
{
using TL = typelist<Ts...>;
using RTL = typename typelist_reverse<TL>::type;
using type = Foo<typename typelist_get<I, RTL>::type...>;
};
template <typename> struct reverse_foo;
template <typename...Ts>
struct reverse_foo<Foo<Ts...>>
: reverse_foo_impl<Foo<Ts...>, make_index_sequence<sizeof...(Ts)>>
{ };
Here, TL are the template parameters of Foo as typelist, RTL is the same typelist reversed. To extract the template parameters as pack, we need to create something like typelist_get<0, RTL>::type, typelist_get<1, RTL>::type, ..., typelist_get<N - 1, RTL>::type. This is accomplished using the index sequence where the expansion w.r.t. I exactly recreates that pattern.
In the end we can use it like this:
using T = Foo<int, char, double>;
using R = reverse_foo<T>::type;
static_assert(std::is_same<Foo<double, char, int>, R>::value, ":(");

Removing the first specified type found from a nested pack

For example,
using NestedPack = Pack<long, char, double, Pack<long, int, short, int>, char, int>;
Then
RemoveFirstFoundFromNestedPack<int, NestedPack>::type
is supposed to give
Pack<long, char, double, Pack<long, short, int>, char, int>
First, I took care of the case of a non-nested pack:
template <typename T> struct Identity { using type = T; };
template <typename, typename, typename...> struct RemoveFirstFoundFromPackHelper;
template <typename RemoveMe, template<typename...> class P, typename... Types>
struct RemoveFirstFoundFromPackHelper<RemoveMe, P<>, Types...> {
using type = P<Types...>;
};
template <typename RemoveMe, template<typename...> class P, typename First, typename... Rest, typename... Types>
struct RemoveFirstFoundFromPackHelper<RemoveMe, P<First, Rest...>, Types...> :
std::conditional<std::is_same<RemoveMe, First>::value,
Identity <P<Types..., Rest...>>,
RemoveFirstFoundFromPackHelper<RemoveMe, P<Rest...>, Types..., First>
>::type {};
template <typename, typename> struct RemoveFirstFoundFromPack;
template <typename RemoveMe, template<typename...> class P, typename... Types>
struct RemoveFirstFoundFromPack<RemoveMe, P<Types...>> : RemoveFirstFoundFromPackHelper<RemoveMe, P<Types...>> {};
This was tested to work correctly (using std::is_same). But I'm stuck with the nested case. This was my latest attempt, which gives incorrect results (though I couldn't trace why):
template <typename>
struct IsPack : std::false_type {};
template <template<typename...> class P, typename... Types>
struct IsPack<P<Types...>> : std::true_type {};
template <typename...> struct MergePacks;
template <typename Pack>
struct MergePacks<Pack> : Identity<Pack> {};
template <template <typename...> class P, typename... Types1, typename... Types2, typename... Packs>
struct MergePacks<P<Types1...>, P<Types2...>, Packs...> : MergePacks<P<Types1..., Types2...>, Packs...> {};
template <typename, typename, typename> struct RemoveFirstFoundFromNestedPackHelper;
template <typename RemoveMe, template<typename...> class P, typename... Types>
struct RemoveFirstFoundFromNestedPackHelper<RemoveMe, P<>, P<Types...>> {
using type = P<Types...>;
};
template <typename RemoveMe, template<typename...> class P, typename First, typename... Rest, typename... Types>
struct RemoveFirstFoundFromNestedPackHelper<RemoveMe, P<First, Rest...>, P<Types...>> :
std::conditional<std::is_same<RemoveMe, First>::value,
Identity <P<Types..., Rest...>>,
typename std::conditional<IsPack<First>::value,
RemoveFirstFoundFromNestedPackHelper<RemoveMe, P<Rest...>, typename MergePacks<P<Types...>,
typename RemoveFirstFoundFromPack<RemoveMe, First>::type>::type>,
RemoveFirstFoundFromNestedPackHelper<RemoveMe, P<Rest...>, P<Types..., First>>
>::type
>::type {};
template <typename, typename> struct RemoveFirstFoundFromNestedPack;
template <typename RemoveMe, template<typename...> class P, typename... Types>
struct RemoveFirstFoundFromNestedPack<RemoveMe, P<Types...>> :
RemoveFirstFoundFromNestedPackHelper<RemoveMe, P<Types...>, P<>> {};
I know there must be a better way than this.
Of course, once this problem is solved, then removing all instances of the specified type found from the nested pack should be just a simple variation of the solution (I've already done that for the non-nested case).
Your IsPack trait would match pretty much every template under the sun as long as it doesn't have a non-type parameter, which is a rather bad idea.
template <class...> struct Pack {};
template <class, class, class = Pack<>> struct Remove_First;
// First one isn't a pack, and isn't what we are looking for.
template <class R, class F, class...Args1, class...Args2>
struct Remove_First<R, Pack<F, Args1...>, Pack<Args2...>> : Remove_First<R, Pack<Args1...>, Pack<Args2..., F>> {};
// First one is the type we are looking for.
template <class R, class...Args1, class...Args2>
struct Remove_First<R, Pack<R, Args1...>, Pack<Args2...>> { using type = Pack<Args2..., Args1...>; };
// Didn't find the type
template <class R, class...Args>
struct Remove_First<R, Pack<>, Pack<Args...>> { using type = Pack<Args...>; };
// Nested pack: Attempt to remove R from the nested pack
// Use is_same to check if a removal occurred and proceed accordingly
template <class R, class...Args1, class...Args2, class...ArgsNested>
struct Remove_First<R, Pack<Pack<ArgsNested...>, Args1...>, Pack<Args2...>> {
using type = typename std::conditional<
std::is_same<typename Remove_First<R, Pack<ArgsNested...>>::type, Pack<ArgsNested...>>::value,
typename Remove_First<R, Pack<Args1...>, Pack<Args2..., Pack<ArgsNested...>>>::type,
Pack<Args2..., typename Remove_First<R, Pack<ArgsNested...>>::type, Args1...>>::type;
};
// if the type to remove is a Pack, and the first type in the list is a Pack,
// and they are the same pack, then remove it and we are done.
// if they are not the same Pack, then this specialization won't match,
// and we go into the recursion case as normal.
template <class...Args1, class...Args2, class...ArgsNested>
struct Remove_First<Pack<ArgsNested...>, Pack<Pack<ArgsNested...>, Args1...>, Pack<Args2...>> {
using type = Pack<Args2..., Args1...>;
};
template<class R, class Pack>
using remove_first_t = typename Remove_First<R, Pack>::type;
The first three partial specializations handle removal from a non-nested pack. A nested pack is handled by the fourth partial specialization. It recursively performs the removal on the nested pack. If a type is removed (and so the resulting type is not the same as the original nested pack type), then we are done. Otherwise, we simply continue to process the rest of the outer pack as usual. The final specialization handles the case where the type to be removed is a Pack itself, as otherwise when the type is present both the second and the fourth partial specialization will match, and neither is more specialized than the other, resulting in an ambiguity error.
Demo.