3

I'm trying to deduce the underlying template type T from a type E = T<T2,T3>. This would for example make it possible to make a template function pair_maker(const E & a) which can be used with one of several similar types of containers. Rough meta code:

template <typename T>
auto pairmaker(const E & a) -> PairContents<E,std::string>::type {
    ContainerPairMaker<E,std::string>::type output;
    ... some code ...
    return output;
}

PairContents<E,std::string>

would transform the type vector<int> into vector<pair(int,std::string)> or whatever<T1> into whatever<pair(T1,std::string)>.

Another similar example of type dissection is for std::array (or similar containers) where I like to figure out the container type to make a new similar array. For example for these kind of functions (this is actual working code now)

template <typename T > 
auto make_some3(const T & a) 
           -> std::array<typename T::value_type,10*std::tuple_size<T>::value>{   
   return std::array<typename T::value_type,10*std::tuple_size<T>::value>{} ;
}

This works fine but what I'm after is to make the explicit use of 'std::array' automatic.

For std::array there's the tuple_size trait which helps, and a similar thing can be used to find the type for any second argument, but again I can't think of anything for finding the container type.

To summarize: what kind of machinery (if any) can be used for cases like these. To which extent is it possible to deal with mixes of template arguments, template-template arguments, any number of arguments, and non-template arguments of unknown types.

Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • 3
    Problem is, if you *only* rebind the first parameter, standard containers will have a mismatching allocator, aka `PairContent>,float>::type` will be `vector, allocator>`. – Xeo Jul 30 '12 at 14:57
  • As a point of terminology, in `T` `T` is not a type. What you're looking for is the underlying class template `T` of which `T` is a specialization (and a type). Hence you can't have `extract>::type` be `std::vector`. – Luc Danton Jul 30 '12 at 15:00
  • Xeo, Ohh... That's a huge issue. Why don't you bring that into your now deleted answer? – Johan Lundberg Jul 30 '12 at 15:04

3 Answers3

13

Here's an idea:

 template <typename T, typename ...>
 struct tmpl_rebind
 {
     typedef T type;
 };

 template <template <typename ...> class Tmpl, typename ...T, typename ...Args>
 struct tmpl_rebind<Tmpl<T...>, Args...>
 {
     typedef Tmpl<Args...> type;
 };

Usage:

typedef std::vector<int> IV;
typedef typename tmpl_rebind<IV, std::pair<double, std::string>>::type PV;

Now PV = std::vector<std::pair<double, std::string>>.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • I had no idea specified templates could be passed as template template arguments. – Johan Lundberg Jul 30 '12 at 14:57
  • @JohanLundberg They can't -- if you think of matching a template specialization as pattern matching, then here the template specialization is deconstructed into its primary template and template parameters parts. Look at the primary template declaration of `tmpl_rebind`: the first argument (`typename T`) is in fact a template type argument, not a template template argument. – Luc Danton Jul 30 '12 at 15:03
  • @JohanLundberg: That isn't my answer, I only edited a certain part of it. ;) – Xeo Jul 30 '12 at 15:33
  • @JohanLundberg This answer doesn't have that problem. – Luc Danton Jul 30 '12 at 15:35
  • Xeo, Ah, yes I just mistyped and swapped your names. @Xeo, Kerrek, Luc, thank you for these comments. All right, in this case all arguments are rebound, so there's no old allocating in there. I see it now. typeid(PV) == typeid(PV2) – Johan Lundberg Jul 30 '12 at 15:46
  • @Xeo, KerrekSB, I found I liked a slight variant of this solution, I posted it as a self answer for reference. – Johan Lundberg Jul 30 '12 at 22:00
4

This is a self answer I came up with as a variant of the answer from Kerrek SB

It is possible to make a trait that extracts std::vector from std::vector<int> and exposes it as ::type via a trait. Yes, this solution is nearly identical to Kerrek's, but to me the use syntax is more aesthetic, putting the template parameters after ::type.

template <typename T, typename ...>
struct retemplate
{
    typedef T type;
};

template <template <typename ...> class Tmpl, typename ...T>
struct retemplate<Tmpl<T...>>
{
   template <typename ...AR>
   using type=Tmpl<AR...> ;
};

with this you actually get retemplate<T<A,B,C>>::type equal to the template T

example use:

typedef std::vector<int> intvec; 
typedef retemplate<intvec>::type<double> doublevec; 

or to expose the container type

typedef std::vector<int> intv;
template <typename ...T>
using vector_T= retemplate<intv>::type<T...> ;

Note that when using this in template context, an extra template is required just after ::, like this: (elaborating on the comment from Xeo)

template <typename T>
typename retemplate<T>::template type<double> containedDouble(T& a) {
   decltype(containedDouble(a)) out;
   for (auto &i : a)
      out.push_back(i);
   return out;
}

What that does is to take an object of type T1<T2> and copy its content into a T1<double>. For example with T1==std::vector and T2==int.

Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • The calling snytax gets *slightly* ugly inside template code, though: `typename retemplate::template type`. – Xeo Jul 30 '12 at 22:03
  • This is nice. I haven't quite got used to the new `using`s yet :-) – Kerrek SB Jul 30 '12 at 22:23
  • @Xeo: Why do you have to say `template`? There's no dependent name there, is there -- `intv` is an actual type. – Kerrek SB Jul 30 '12 at 22:25
  • @Kerrek: Inside a template, you'd need to (and obviously replace `intv` with a template parameter, my fail). – Xeo Jul 30 '12 at 22:30
  • 1
    @Xeo: Yes, indeed, if you're inside a dependent context, of course. Cheers. Anyway, who doesn't love saying `::template` a lot to impress the boss? – Kerrek SB Jul 31 '12 at 04:54
  • @KerrekSB, that's hilarious, not sure impress is the right word. But really, I never would have figured out to put `template ` between `::` and `type`. – Johan Lundberg Jul 31 '12 at 09:59
  • yes this new `using` template typedef shortens the meta bloat nicely. before we had to declare new template structs which would define a ::type of thmeselves. inheritance from the nesting class never worked for a reason I still don't grasp. thus the new `using` saves 3 lines. and a struct keyword. – v.oddou Apr 10 '15 at 02:23
2

I recommend taking a look at A. Alexandrescu's book Modern C++ Design.

If I recall correctly he explains how one might use type lists to store and access arbitrary types in a list-like fashion. These lists can be used to provide type information in a number of different situations. Take a look at the implementation of Loki to see how type lists can be utilized.

I'm not sure if this is helpful at all, but maybe you can learn something from the ideas used in Loki in order to solve or at least better understand your specific issues at hand.

mtd
  • 123
  • 7