3

Yes.

Let's say I have a simple variadic struct that holds a typedef:

template<typename... TArgs> struct TupleTypeHolder {
    using TupleType = std::tuple<TArgs*...>;
};

I want to pass TupleTypeHolder<something> as a template parameter to another class, and get that typedef.

All of my tries do not compile.

// None of these is valid
template<template<typename...> class TTupleTypeHolder> struct TupleMaker {
     using MyTupleType = TTupleTypeHolder::TupleType; // Not valid
     using MyTupleType = typename TTupleTypeHolder::TupleType; // Not valid
};

 template<template<typename... A> class TTupleTypeHolder> struct TupleMaker2 {
     // A is not a valid name here
     using MyTupleType = TTupleTypeHolder<A...>::TupleType; // Not valid
     using MyTupleType = typename TTupleTypeHolder<A...>::TupleType; // Not valid
};

Is there a way to use the variadic template parameters (in this case, TupleTypeHolder's TArgs...) of a variadic template class from a class that uses the aforementioned class as a template variadic template parameter?


Usage example:

template<typename... TArgs> struct TupleTypeHolder {
    using TupleType = std::tuple<TArgs*...>;
};

template<typename... TArgs> static int getSomeValue() { ... }

template<??? T1, ??? T2> class TupleMaker 
{ 
    std::pair<int, int> someValues;

    using TupleType1 = T1::TupleType;
    using TupleType2 = T2::TupleType;

    TupleMaker() : someValues{getSomeValue<T1's TArgs...>(), 
                             getSomeValue<T2's TArgs...>()} { }
};

class MyTupleMaker : TupleMaker<TupleTypeHolder<int, char>, 
                                TupleTypeHolder<int, float>> 
{ };

MyTupleMaker::TupleType1 tuple1{new int(1), new char('a')};  
MyTupleMaker::TupleType2 tuple1{new int(35), new float(12.f)};  
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 2
    1) `TTupleTypeHolder` is a template, not a type. 2) You have no access to `A` (you pass a template, without arguments, so there are no arguments to deduce). – dyp Oct 10 '13 at 14:25
  • In your code `A` has no meaning. It is not a parameter. It is just a name, without any meaning. It is more like a part of the *signature* of the template template parameter. – Nawaz Oct 10 '13 at 14:26
  • Please show a usage example (an example of the intended usage of `TupleMaker`). – dyp Oct 10 '13 at 14:26
  • @DyP: Added an usage example. Nawaz: I am aware of that, I was just trying to show what I had tried (without succeeding). – Vittorio Romeo Oct 10 '13 at 14:30
  • I don't see why you need the template arguments of `TupleTypeHolder` in `TupleMaker`. `template struct TupleMaker { using TupleType1 = typename T1::TupleType; using TupleType2 = typename T2::TupleType; };` should work fine. (Oh, and public inheritance for `MyTupleMaker`.) – dyp Oct 10 '13 at 14:32
  • 1
    +1 for the title, if nothing else (but interesting question, too). – Angew is no longer proud of SO Oct 10 '13 at 14:33
  • @DyP: sorry, my example was flawed. Updated it again – Vittorio Romeo Oct 10 '13 at 14:36
  • Then, partial specialization or `std::tuple_element`. – dyp Oct 10 '13 at 14:37
  • *Hint:* For quick examples, use `struct` rather than `class`. You won't have to explicitly write the `public` access specifiers. Here, they're necessary to make the using-declarations in `TupleMaker` and the inheritance of `MyTupleMaker` public. – dyp Oct 10 '13 at 14:47
  • 1
    Needs more variardic bell. – Yakk - Adam Nevraumont Oct 10 '13 at 19:16

2 Answers2

5

Working usage example:

#include <tuple>

template<typename... TArgs> struct TupleTypeHolder {
    using TupleType = std::tuple<TArgs*...>;
};

template<typename... TArgs> static int getSomeValue() { return 42; }

// primary template:
template<class T1, class T2>
struct TupleMaker;

// partial specialization:
template<template<class...> class TT1, template<class...> class TT2,
         class... T1, class... T2>
struct TupleMaker < TT1<T1...>, TT2<T2...> >
{ 
    std::pair<int, int> someValues;

    using TupleType1 = typename TT1<T1...>::TupleType;
    using TupleType2 = typename TT2<T2...>::TupleType;

    TupleMaker() : someValues{getSomeValue<T1...>(), 
                              getSomeValue<T2...>()} { }
};

struct MyTupleMaker : TupleMaker<TupleTypeHolder<int, char>, 
                                TupleTypeHolder<int, float>> 
{ };

MyTupleMaker::TupleType1 tuple1{new int(1), new char('a')};  
MyTupleMaker::TupleType2 tuple2{new int(35), new float(12.f)};  

int main() {}

The primary template takes two types, as you're passing types. TupleTypeHolder<int, char> is a type, a specialization of a template, not a template itself. Template template-parameters however take templates as arguments (not types), such as:

template<template<class...> class Foo>
struct Bar
{
    using type = Foo<int, double, char>;
};

Bar< std::tuple > b; // note: no template arguments for `std::tuple`!

With partial specialization, you can split a template specialization into the template and the parameters, that's how the above works.

dyp
  • 38,334
  • 13
  • 112
  • 177
  • Great answer! I wasn't even aware you could do such tricks as passing two variadic type packs with partial template specialization, because on a main template type there is a restriction that variadic parameters must be at the end and the compiler does not allow two packs. I am seriously enlightened! –  Oct 10 '13 at 15:00
  • ... whereas I feel like I _should_ have become seriously enlightened, but fell asleep half-way through instead. +1 – Lightness Races in Orbit Oct 10 '13 at 23:29
2

Template-template parameters are not really type parameters, are parameters to specify a template. That means what you pass through a template-template parameter is not a type, is a template:

template<template<typename> class TPARAM>
struct give_me_a_template
{
    using param = TPARAM; //Error TPARAM is not a type, is a template.
    using param_bool = TPARAM<bool>; //OK, thats a type
};

As you can see, the first alias is invalid, because TPARAM is not a type, is a template. But the second is a type (Is an instance of the template).

That said, examine your problem: What you called TupleTypeHolder could be viewed as a variadic-template typelist. So your goal is to make tuples of the types specified with typelists, right?

You can use partial specialization to extract the content of a typelist:

template<typename TupleTypeHolder>
struct tuple_maker;

template<typename... Ts>
struct tuple_maker<TupleTypeHolder<Ts...>>
{
    using tuple_type = std::tuple<Ts...>;
};

An example of its usage could be:

using my_types = TupleTypeHolder<int,int,int>;
using my_tuple_type = typename tuple_maker<my_types>::tuple_type;

Of course this is not the exactly solution to your implementation, you need to extend the concept to multiple typelists (As your question showed). What I have provided is the guide to understand the problem and its solution.

Manu343726
  • 13,969
  • 4
  • 40
  • 75