template template parameter expansion for variadic templates
Here's a solution that doesn't require pre-packing the template template-arguments as tuples. This packing is done automatically, you only have to provide how many arguments are to be packed in one tuple (N
).
#include <tuple>
template<template<class...> class Container, int N>
struct join_n_impl
{
template<class ArgTuple, int I = 0, class Joined = std::tuple<>>
struct helper;
template<class Arg, class... Rest, int I, class... Joined>
struct helper<std::tuple<Arg, Rest...>, I, std::tuple<Joined...>>
: helper<std::tuple<Rest...>, I+1, std::tuple<Joined..., Arg>>
{};
template<class Arg, class... Rest, class... Joined>
struct helper<std::tuple<Arg, Rest...>, N, std::tuple<Joined...>>
{
using type = Container<Joined...>;
using rest = std::tuple<Arg, Rest...>;
};
template<class... Joined>
struct helper<std::tuple<>, N, std::tuple<Joined...>>
{
using type = Container<Joined...>;
using rest = std::tuple<>;
};
};
template<template<class...> class Container, int N, class ArgTuple>
using join_n = typename join_n_impl<Container, N>::template helper<ArgTuple>;
template<template<class...> class Container, int N, class Args,
class Collected = std::tuple<>>
struct pack_n;
template<template<class...> class Container, int N, class... Args,
class... Collected>
struct pack_n<Container, N, std::tuple<Args...>, std::tuple<Collected...>>
{
static_assert(sizeof...(Args) % N == 0,
"Number of arguments is not divisible by N.");
using joiner = join_n<Container, N, std::tuple<Args...>>;
using joined = typename joiner::type;
using rest = typename joiner::rest;
using type = typename pack_n<Container, N, rest,
std::tuple<Collected..., joined>>::type;
};
template<template<class...> class Container, int N, class... Collected>
struct pack_n<Container, N, std::tuple<>, std::tuple<Collected...>>
{
using type = std::tuple<Collected...>;
};
Usage example:
template<class, class>
struct test {};
#include <iostream>
template<class T>
void print_type(T) { std::cout << __PRETTY_FUNCTION__ << "\n"; }
int main()
{
using to_pack = std::tuple<int, double, int, char, int, bool>;
print_type( pack_n<test, 2, to_pack>::type{} );
}
It is not possible according to your first attempt, but it is possible according to your edit, where arguments are packed within std::tuple
's. In this case, template Embed
below takes arguments in each tuple
and embeds them in Container
.
See live example.
template<template<class... > class Container, typename P>
struct Embed_t;
template<template<class... > class Container, typename... T>
struct Embed_t <Container, std::tuple <T...> >
{
using type = Container <T...>;
};
template<template<class... > class Container, typename P>
using Embed = typename Embed_t <Container, P>::type;
template<template<class... > class Container, typename... P>
struct ContainerTemplate
{
using container = std::tuple<Embed <Container, P>...>;
};
In general, placing ...
within ...
is very tricky and can happen only in limited circumstances (I've only managed this once in a useful way).
So i actually managed to find a way to solve my Question. I will leave iavr's answer as soloution though since the syntax is nice and it also allows the use of template overloads. So just for completeness sake and to prove that it is indeed possible:
template<typename... T>
struct TypeList
{
static const size_t Size = sizeof...(T);
template<typename T2>
struct PushFront
{
typedef TypeList<T2, T...> type_list;
};
};
template<template<class...> class Template, typename... Args>
struct SizeofTemplateTemplate
{
static const size_t Size = 0;
typedef TypeList<> type;
};
template<template<class...> class Template, typename Arg, typename... Args>
struct SizeofTemplateTemplate<Template, Arg, Args...>
{
typedef char yes[1];
typedef char no[2];
template<typename...>
struct Test;
template<typename... args>
struct Test<TypeList<args...>>
{
template<template<class...> class Testee>
static yes& TestTemplate(Testee<args...>* arg);
template<template<class...> class Testee>
static no& TestTemplate(...);
};
typedef typename SizeofTemplateTemplate<Template, Args...>::type::PushFront<Arg>::type_list type;
static const size_t Size = sizeof(Test<type>::TestTemplate<Template>(0)) == sizeof(yes) ? type::Size : SizeofTemplateTemplate<Template, Args...>::Size;
};
template<template<class...> class Template, size_t N, typename... Args>
struct GenerateNTuple;
template<template<class...> class Template, typename... Args>
struct GenerateNTuple<Template, 0, Args...>
{
using type = TypeList<>;
using rest = TypeList<Args...>;
};
template<template<class...> class Template, size_t N, typename Head, typename... Args>
struct GenerateNTuple<Template, N, Head, Args...>
{
using type = typename GenerateNTuple<Template, N - 1, Args...>::type::template PushFront<Head>::type_list;
using rest = typename GenerateNTuple<Template, N - 1, Args...>::rest;
};
template<template<class...> class Container, typename... args>
struct DeduceType;
template<template<class...> class Container, typename... args>
struct DeduceType<Container, TypeList<args...>>
{
using type = Container<args...>;
};
template<template<class...> class Template, typename... Args>
struct ContainerTemplate;
template<template<class...> class Template, typename... Args>
struct ContainerTemplate<Template, TypeList<Args...>>
{
using packed = GenerateNTuple<Template, SizeofTemplateTemplate<Template, Args...>::Size, Args...>;
using type = typename ContainerTemplate<Template, typename packed::rest>::type::template PushFront<typename DeduceType<Template, typename packed::type>::type>::type_list;
};
template<template<class...> class Template>
struct ContainerTemplate<Template, TypeList<>>
{
using type = TypeList<>;
};
template<template<class...> class Template, typename... Args>
using ContainerTypeList = typename ContainerTemplate<Template, TypeList<Args...>>::type;
usage is like this:
template<typename T>
using vec = std::vector<T>;
std::cout << typeid(ContainerTypeList<vec, int, short>).name() << std::endl;