12

I recently learned about the existence of template template parameters and was now wondering if something like this would be possible:

template<template<class... > class Container, typename... args>
struct ContainerTemplate
{
    using container = std::tuple<Container<args...>...>;
};

what i want is a template that gets a Container or some other template class as a template template parameter and then expands the rest of the template arguments in such a way that if Container has N template args and i give N * M template arguments for args i get M template instantiations with N template args eg:

ContainerTemplate<std::vector, int, short, char>
//assuming std::vector takes only 1 arg for simplicity    

should result in

container = std::tuple<std::vector<int>, std::vector<short>, std::vector<char>>

while

ContainerTemplate<std::map, int, int, short, short>
//assuming std::map takes only 2 args for simplicity    

should result in

container = std::tuple<std::map<int, int>, std::map<short, short>>

Is there any way to do this? The question would be wether you could find out how many template args Container takes or not.

Edit: it would be ok if you were required to pass the additional arguments in tuples of size N

ContainerTemplate<std::map, std::tuple<int, int>, std::tuple<short, short>>

Edit2: so i actually found a way to determine the number of template template arguments

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...>
{
    template<typename... Args>
    struct Test;

    typedef char yes[1];
    typedef char no[2];

    template<typename... Args>
    struct Test<TypeList<Args...>>
    {
        template<template<class...> class Template>
        static yes& TestTemplate(Template<Args...>* arg);

        template<template<class...> class Template>
        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;
};

with this, the following code will print 2

std::cout << SizeofTemplateTemplate<std::vector, int, std::allocator<int>, int, int>::Size << std::endl;

only problem i have now is that dyp's solution crashes the visual studio compiler xD

Edit3: complete solution for the original question here: https://stackoverflow.com/a/22302867/1366591

Community
  • 1
  • 1
ACB
  • 1,607
  • 11
  • 31
  • 1
    The first one? Easily. The second one? Wat. How would it be supposed to know the number of arguments it's supposed to pass to that type? (Do note that standard containers have *more* template arguments than you seem to think they have). – Griwes Mar 09 '14 at 23:49
  • i know they have more that was just for simplification. the question i have is exactly if it would possible to determine the number of template arguments in any way and then salvage that info to get what i want – ACB Mar 09 '14 at 23:51
  • 1
    The compiler cannot read your mind, sorry. – Griwes Mar 09 '14 at 23:52
  • @Griwes Thats why he want to know a way to tell him... – Sebastian Hoffmann Mar 09 '14 at 23:53
  • I could see there being a way to make, say, a version that's simply greedy and uses SFINAE to ignore ones that don't work, but I wouldn't know where to begin on that. – chris Mar 09 '14 at 23:53
  • well the compiler does know that template struct MyVector; has 1 template argument. So i am asking if there is a way to get that info. – ACB Mar 09 '14 at 23:53
  • @Griwes You could easily fix that by doing the following: `template using vec = std::vector;` and passing vec instead of std::vector. – typ1232 Mar 09 '14 at 23:57
  • @typ1232, right, that can be onto something. – Griwes Mar 10 '14 at 00:14
  • thats not really the point of the question though ^^ – ACB Mar 10 '14 at 00:15
  • Hmm I don't think you can deduce the number of template template-parameters in the general case; I have an [unformatted solution](http://coliru.stacked-crooked.com/a/ef4902f3507608a0) if you explicitly pass the number of arguments to consume per step. – dyp Mar 10 '14 at 00:16

7 Answers7

7

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).

iavr
  • 7,547
  • 1
  • 18
  • 53
  • ye found that aswell for tuple ^^ well seems you cant deduce the number of template template args so this is as good as it gets – ACB Mar 10 '14 at 00:26
7

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{} );
}
dyp
  • 38,334
  • 13
  • 112
  • 177
  • nice solution. but iavr's version works for N template template parameters aswell^^. too bad you have to specify the number of template template args though. but thanks for the idea – ACB Mar 10 '14 at 00:38
  • @ACB Oh you're right. But you'll have to pre-pack them as tuples as far as I can see. – dyp Mar 10 '14 at 00:40
  • ye i know. but it works ^^ and as a side effect you have some syntax to tell which types belong together – ACB Mar 10 '14 at 00:48
  • Write a traits class on the template and args that returns how many args it will accept? Then use it to deduce `N`? Might have to do a level of indirection. @ACB I think such a trait is writeable... – Yakk - Adam Nevraumont Mar 10 '14 at 02:31
  • since i now found a way to determine the number of args you need. do you have any idea why the visual studio compiler crashes with your code ? – ACB Mar 10 '14 at 03:01
  • @ACB A crash while compiling always implies there's a compiler bug. – dyp Mar 10 '14 at 11:43
6

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;
ACB
  • 1,607
  • 11
  • 31
  • Very nice! Feel free to chose this answer over mine :-) It's the only one that actually answers the question, how to automatically find the number of template parameters. Now this may only fail possibly when we occasionally define for different reasons `template struct ...` for something that actually takes only 1-2 parameters. Not sure, but you could check. – iavr Mar 13 '14 at 18:23
3

I've come up with another solution that does fully automatic packing according to your first requirement. The caveat is that implementation is not fully variadic: you have to specialize for template templates of 1, 2, 3 arguments etc. However, usage is exactly as you required initially.

This is probably similar to dyp's solution which I did not study very carefully.

Again, see live example.

In short, pack template templates into plain templates like that:

template<template<class> class>
struct Temp1;

template<template<class, class> class>
struct Temp2;

Then, the main definition of ContainerTemplate e.g. for 2 arguments is

template<
    template<class, class> class Container,
    typename T1, typename T2, typename... T
>
struct ContainerTemplate <Temp2<Container>, T1, T2, T...>
{
    using container = Join <
        std::tuple<Container<T1, T2> >,
        typename ContainerTemplate<Temp2<Container>, T...>::container
    >;
};

template<template<class, class> class Container>
struct ContainerTemplate<Temp2<Container> >
{
    using container = std::tuple<>;
};

where Join is concatenation (see live example for definition).

Finally, given e.g.

template<class> class Vector { };
template<class, class> class Map { };

usage is pretty nice:

ContainerTemplate<Temp1<Vector>, int, short, char>
ContainerTemplate<Temp2<Map>, int, int, short, short>
iavr
  • 7,547
  • 1
  • 18
  • 53
  • specializing kinda defeats the point of the exercise ^^ looks like a real oversight to me that you can't sizeof...(template template args) – ACB Mar 10 '14 at 01:36
  • You're right, myself I would prefer my first solution, although its usage is a bit more cumbersome. In general a template template with specific number of arguments has many weaknesses, so I tend to *always* use a `template class`. Note also that since there is no checking, using such a template with the wrong number of arguments may give all sorts of unexpected results. – iavr Mar 10 '14 at 08:57
3

Here's a start using Boost Mpl.

I opted to solve the map case by first 'pairing' the input into a vector of mpl::pair.

#include <boost/mpl/transform.hpp>
#include <boost/mpl/push_front.hpp>
#include <boost/mpl/pair.hpp>
#include <boost/mpl/vector.hpp>
#include <vector>
#include <map>

namespace mpl = boost::mpl;

namespace detail
{
    using namespace mpl;

    template <template <typename...> class Container, typename... T>
        using unary = typename transform<vector<T...>, Container<_1> >::type;

    namespace binary_impl
    {
        template <typename MplVector> struct pairs;

        template <> struct pairs<mpl::vector<> >
        {
            using type = mpl::vector<>;
        };

        template <typename A, typename B, typename... T>
            struct pairs<mpl::vector<A, B, T...> >
        {
            using type = typename mpl::push_front<
                    typename pairs<mpl::vector<T...> >::type,
                    mpl::pair<A, B>
                >::type;
        };
    }

    template <template <typename...> class Container, typename... T>
        using binary = typename transform<
            typename binary_impl::pairs<vector<T...> >::type, 
            Container<apply_wrap1<first<>, _1>, apply_wrap1<second<>, _1> >
            >
            ::type;
}

template <typename K, typename V, typename stuff = std::less<K> >
struct MyMap : std::map<K,V,stuff> { using std::map<K, V>::map; };

template <typename... T> using make_vectors = detail::unary<std::vector, T...>;
template <typename... T> using make_pairs   = detail::binary<std::pair,  T...>;
template <typename... T> using make_mymaps  = detail::binary<MyMap,      T...>;

#include <iostream>
#include <string>

int main()
{
    auto vectors = make_vectors<int, char, double> { };
    auto pairs   = make_pairs  <int, char, int, std::string, int, double> { };
    auto mymaps  = make_mymaps <int, char, int, std::string, int, double> { };
}

For some reason, it will not work with actual std::map but it will with my std::pair or my own (std::map<> derived) MyMap type. (If anyone can explain the reason here, I'd be very happy to know).

See it Live On Coliru

sehe
  • 374,641
  • 47
  • 450
  • 633
0

Here is another variation using std::tuple. I used some code from @ACB for the template parameter count calculation.

#include <tuple>

template<template<typename...> class Template, typename... Args>
struct TemplateArgCount
{
   static const int value = 0;
};

template<template<typename...> class Template, typename Arg, typename... Args>
struct TemplateArgCount<Template, Arg, Args...>
{
   typedef char small[1];
   typedef char big[2];

   template<typename... A>
   struct Test
   {
      template<template<typename...> class T>
      static small& test(T<A...>*);

      template<template<typename...> class T>
      static big& test(...);
   };

   static const int value = sizeof(Test<Arg, Args...>::template test<Template>(0)) == sizeof(small)
                            ? sizeof...(Args)+1
                            : TemplateArgCount<Template, Args...>::value;
}; 

template<typename GlobalResult, typename LocalResult, template<typename...> class Template, int Count, int Pos, typename... Args>
struct TemplateTuplesImpl;

template<typename... GlobalResult, typename... LocalResult, template<typename...> class Template, int Count, typename Arg, typename... Args>
struct TemplateTuplesImpl<std::tuple<GlobalResult...>, std::tuple<LocalResult...>, Template, Count, Count, Arg, Args...>
: TemplateTuplesImpl<std::tuple<GlobalResult..., Template<LocalResult...>>, std::tuple<>, Template, Count, 0, Arg, Args...>
{
};

template<typename GlobalResult, typename... LocalResult, template<typename...> class Template, int Count, int Pos, typename Arg, typename... Args>
struct TemplateTuplesImpl<GlobalResult, std::tuple<LocalResult...>, Template, Count, Pos, Arg, Args...>
: TemplateTuplesImpl<GlobalResult, std::tuple<LocalResult..., Arg>, Template, Count, Pos+1, Args...>
{
};

template<typename... GlobalResult, typename ...LocalResult, template<typename...> class Template, int Count>
struct TemplateTuplesImpl<std::tuple<GlobalResult...>, std::tuple<LocalResult...>, Template, Count, Count>
{
   using type = std::tuple<GlobalResult..., Template<LocalResult...>>;
};

template<template<class... Params> class Container, typename... Args>
struct TemplateTuples
{
   static const int ParamSize = TemplateArgCount<Container, Args...>::value;
   static const int ArgSize = sizeof...(Args);
   static_assert(ParamSize > 0, "Arguments list does not match template class param list!");
   static_assert(ArgSize%ParamSize == 0, "Argument list not in multiples of template class param count!");
   using type = typename TemplateTuplesImpl<std::tuple<>, std::tuple<>, Container, ParamSize, 0, Args...>::type;
};

Usage is like this:

#include <type_traits>
#include <utility>

int main()
{
   static_assert(std::is_same<TemplateTuples<std::pair, int, short, float, double>::type, 
                              std::tuple<std::pair<int, short>, std::pair<float, double>>
                             >::value, "Does not match :-(");
   return 0;
}
0

After fiddling around with various solutions from this thread I decided on this solution:

A two-dimensional "tuple pack", i.e. tuple< tuple< T1 >, tuple<T2,T3>, ... > etc.

This allows a couple of things:

  1. I'd like to not use a container, but there is no such thing as "default variadic parameters" so the tuple system allows default parameters for a 2D tuple pack merely by not being a variadic type-pack.
  2. Each sub-tuple can have a different number of arguments if desired. This allows any default template parameters to come into play - i.e. in cases where the sub-tuple is less specified than the referenced template-template (t_tempMPlex).
  3. Also I'd like to place the result in any holder that might be capable of holding the result - i.e. tuple, variant, or any other variadic holder that a user might want to fill with types (t_tempVarHolder).
// Multiplex templates with 2-dimensional tuple-types and contain them in some type of variant/tuple/etc container.
template < template<class... > class t_tempMPlex, class t_TyTpTPack >
struct  _MPlexTPack2DHelp_2;
template < template<class... > class t_tempMPlex, class ... t_TysExtractTPack >
struct  _MPlexTPack2DHelp_2< t_tempMPlex, tuple< t_TysExtractTPack ... > >
{
  typedef t_tempMPlex< t_TysExtractTPack ... > type;
};
template< template<class... > class t_tempMPlex, class t_TyTp2DTPack, 
          template < class ... > class t_tempVarHolder >
struct _MPlexTPack2DHelp;
template< template<class... > class t_tempMPlex, class ... t_TyTpsExtractTPack, 
          template < class ... > class t_tempVarHolder >
struct _MPlexTPack2DHelp<t_tempMPlex, tuple< t_TyTpsExtractTPack ... >, t_tempVarHolder >
{
  using type = t_tempVarHolder< typename _MPlexTPack2DHelp_2< t_tempMPlex, t_TyTpsExtractTPack >::type ... >;
};
template< template<class... > class t_tempMPlex, class t_TyTp2DTPack, 
          template < class ... > class t_tempVarHolder = tuple >
struct MPlexTPack2D
{
    using type = typename _MPlexTPack2DHelp< t_tempMPlex, t_TyTp2DTPack, t_tempVarHolder >::type;
};
template< template<class... > class t_tempMPlex, class t_TyTp2DTPack, 
          template < class ... > class t_tempVarHolder = tuple >
using MPlexTPack2D_t = typename MPlexTPack2D< t_tempMPlex, t_TyTp2DTPack, t_tempVarHolder >::type;

Usage: Here's my usage scenario: I am writing an XML parser that works natively in any character type. I also want to support switching the endian nature of the file in the scenarios that matter - i.e. for UTF32BE, and UTF16BE - of course when I am on a little endian machine.

So I have these transport types:

template < class t_TyChar, class t_TyBoolSwitchEndian = false_type >
class _l_transport_file;
template < class t_TyChar, class t_TyBoolSwitchEndian = false_type >
class _l_transport_fixedmem;
template < class t_TyChar, class t_TyBoolSwitchEndian = false_type >
class _l_transport_mapped;

I want to provide a default argument for my variant parser that implements all character types and endian-switching possibilities, but I also want the user to be able to specify that they want less than that.

Here is the declaration of my xml_parser_var:

template <  template < class ... > class t_tempTyTransport, 
            class t_TyTp2DCharPack >
class xml_parser_var;

Where t_tempTyTransport is one of the above _l_transport_* templates.

I will provide a default argument of this for t_TyTp2DCharPack:

tuple< tuple< char32_t, true_type >, 
       tuple< char32_t, false_type >, 
       tuple< char16_t, true_type >, 
       tuple< char16_t, false_type >, 
       tuple< char8_t, false_type > >

But I'd like the user to be able to specify less - i.e. perhaps the user programmer doesn't care about UTF32 files, only UTF16 and UTF8. A significant amount of binary space will be saved in the variant if the UTF32 character types are removed.

Anyway, short story long, this is what I came up with. I like it. It allows default arguments to come into play, i.e. this is the same as the above given the default arguments:

tuple< tuple< char32_t, true_type >, 
       tuple< char32_t >, 
       tuple< char16_t, true_type >, 
       tuple< char16_t >, 
       tuple< char8_t > >

Here is my usage sequence:

typedef MPlexTPack2D_t< t_tempTyTransport, t_TyTp2DCharPack > _TyTpTransports;

_TyTpTransports ends up being:

tuple< t_tempTyTransport< char32_t, true_type >, t_tempTyTransport< char32_t >
       t_tempTyTransport< char16_t, true_type >, t_tempTyTransport< char16_t >,
       t_tempTyTransport< char8_t > >

Then that "tuple pack" can be use to produce further typedefs, etc. Also, if I want a variant instead of a tuple, i.e.:

variant< t_tempTyTransport< char32_t, true_type >, t_tempTyTransport< char32_t >
         t_tempTyTransport< char16_t, true_type >, t_tempTyTransport< char16_t >,
         t_tempTyTransport< char8_t > >

Then I use this instead:

typedef MPlexTPack2D_t< t_tempTyTransport, t_TyTp2DCharPack, variant > _TyTpTransports;

Synposis:

typedef MPlexTPack2D_t< variant, 
                        tuple< tuple< char32_t, true_type >, 
                               tuple< char32_t >,  
                               tuple< char16_t, true_type >, 
                               tuple< char16_t >, 
                               tuple< char8_t, false_type > > > _TyTuple2D;
static_assert( is_same_v< _TyTuple2D, 
                          tuple< variant< char32_t, true_type >, 
                                 variant< char32_t >,  
                                 variant< char16_t, true_type >, 
                                 variant< char16_t >, 
                                 variant< char8_t, false_type > > > );

Let me know what y'all think.

David Bien
  • 622
  • 5
  • 10