5

Q

In a dummy example of a class

typedef myStruct<myStruct<myStruct<int>>> mv;

int is the innermost template parameter. How can I get the type of that parameter for arbitrary nesting depth?

Desired Result

A mechanism to acquire the innermost type

innermost<mv>::type -> int

WishList

  1. Can this be done using template aliases (template template parameters are a missing feature here)?

  2. In an example where my type would be

    vector<vector<vector<int>>>
    

    Is there a way to perform the same operation, given that vector expects an extra template parameter ? Ofcourse a distinct implementation could be divised but is there a way to scale the solution for the first problem to handle these cases as well ?

Nikos Athanasiou
  • 29,616
  • 15
  • 87
  • 153
  • What is the use case exactly ? (I suspect an XY problem). C++ doesnt support reflection so there is no general/standard solution to this – quantdev Aug 07 '14 at 16:18
  • 1
    @quantev Purely a metaprogramming quiz to solve exactly what it says. It's a kind of **spot the expert** exercise (even though I kind of already know the 3 people that can easily answer this - you can watch such questions in my profile to see the usual suspects) – Nikos Athanasiou Aug 07 '14 at 16:23
  • What of `vector>`, should the inner type be `std::pair` ? Or are mixed templates not allowed ? As it is, it feels underspecified. – Matthieu M. Aug 07 '14 at 17:03
  • @MatthieuM. No, they're not allowed, we'd have branching solutions – Nikos Athanasiou Aug 08 '14 at 06:52
  • @NikosAthanasiou: So, to sum up: it's *only* the first template parameter (not matter how many), and the solution only needs to work if the template class is always the same. Does the solution need to verify that the template class is always the same (seems difficult, but maybe doable). – Matthieu M. Aug 08 '14 at 07:48
  • 1
    @MatthieuM. Yes, even though I must admit I wasn't thinking of varying inner templates; `vector>>` doesn't look like an absurd problem, but wasn't asking for neither the verification nor the extension to varying inner templates (since you thought of the extra problem it would be interesting to see an approach) – Nikos Athanasiou Aug 08 '14 at 09:10
  • 2
    @Nikos, Thanks for posting this "metaprogramming quiz", it just helped me with a real world problem. I'm working on a class wrapper for V8 like the late https://code.google.com/p/v8-juice/wiki/ClassWrap and this solves the problem where I want to use Opt to flag optional parameters for method calls. Thanks also to 0x499602D2 and Matthieu M for the answers. – Jerry Aug 08 '14 at 18:02

2 Answers2

8

Try the following. It also returns a tuple if the template has more than one element:

#include <tuple>
#include <type_traits>

template<typename T>
struct innermost_impl
{
    using type = T;
};

template<template<typename> class E, typename T>
struct innermost_impl<E<T>>
{
    using type = typename innermost_impl<T>::type;
};

template<template<typename...> class E, typename... Ts>
struct innermost_impl<E<Ts...>>
{
    using type = std::tuple<typename innermost_impl<Ts>::type...>;
};

template<typename T>
using innermost = typename innermost_impl<T>::type;

template<class>
struct X;

static_assert(std::is_same<innermost<X<X<X<int>>>>, int>::value, "");

int main()
{
}
David G
  • 94,763
  • 41
  • 167
  • 253
  • Cool. Would be nice if it worked for non-template types as well ... (so it could be generally used inside templates), in which case the non-template type is returned (or not ?) – quantdev Aug 07 '14 at 16:30
  • @quantdev You'd probably have to make a bunch of specializations for templates that take non-types. Unfortunately there's no template construct as of yet that can represent non-types. – David G Aug 07 '14 at 16:34
  • This does not actually work with `X` being `std::vector` ([see here](http://ideone.com/udjfMi)), you would have to revise the handling of `E` to only use the first element there; but then the behavior on `map` might be surprising. – Matthieu M. Aug 07 '14 at 17:06
  • @MatthieuM. I know. Have a solution? – David G Aug 07 '14 at 17:07
  • Not until the desired behavior for `std::map` is specified. If we always take the first parameter it's easy, but if what we really want is the inner *element* in this case, I see no generic way :x – Matthieu M. Aug 07 '14 at 17:14
8

Building on 0x499602D2's answer, we get the following to only consider the first template parameter if ever there are several. And yes it compiles. It's also slightly simpler.

#include <tuple>
#include <type_traits>
#include <vector>

template<typename T>
struct innermost_impl
{
    using type = T;
};

template<template<typename...> class E, typename Head, typename... Tail>
struct innermost_impl<E<Head, Tail...>>
{
    using type = typename innermost_impl<Head>::type;
};

template<typename T>
using innermost = typename innermost_impl<T>::type;

template<class>
struct X;

static_assert(std::is_same<innermost<X<X<X<int>>>>, int>::value, "");

static_assert(
    std::is_same<innermost<std::vector<X<std::vector<int>>>>, int>::value,
    ""
);

int main()
{
}

Note that no attempt is made to validate that the same template is used over and over.

Community
  • 1
  • 1
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722