8

I want to use the types of a template parameter pack as parameters to a different template, but cut off the last parameter.

For example:

template <class... Ts> struct some_template;

template <class... Ts> struct foo
{
    using bar = some_template<magically_get_all_but_last(Ts)...>;
};

// I might be missing a few "typename"s, but you get the idea.
static_assert(std::is_same<foo<int, bool, std::string>::bar, some_template<int,bool> >::value); 

Note that this is the opposite of getting only the last parameter.

Community
  • 1
  • 1
Tobias Brandt
  • 3,393
  • 18
  • 28

3 Answers3

7

Here is a simple approach which use std::tuple_element<I, Tuple> together with std::index_sequence<sizeof...(Ts) - 1 to get all but the last type in a list of variadic arguments. Since the parameter pack for the indices is needed, there is an extra indirection which is put into a base but could be anywhere.

template <class T, class... Ts> struct foobase;
template <std::size_t... I, class... Ts>
struct foobase<std::index_sequence<I...>, Ts...> {
    using bar = some_template<typename std::tuple_element<I, std::tuple<Ts...>>::type...>;
};

template <class... Ts> struct foo
    : foobase<std::make_index_sequence<sizeof...(Ts) - 1>, Ts...>
{
};
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • 2
    `std::index_sequence` and family can be implemented with C++11. They were just not part of the standard library. So I'd recommend creating your own version. The [proposal n3658](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3658.html) points, e.g., at [this approach](http://stackoverflow.com/a/14059084/1120273). – Dietmar Kühl Aug 15 '15 at 12:54
  • 1
    @tobias note that for extremely long lists, the make index sequence linked is less than ideal: for 10-20ish, it is fine. – Yakk - Adam Nevraumont Aug 15 '15 at 13:26
5

Here is my solution which uses C++11:

template <typename ...P>
struct dummy {};

template <template <typename ...> class Obj, typename T, typename ...P>
struct internal;

template <template <typename ...> class Obj, typename ...P1, typename T, typename ...P2>
struct internal<Obj, dummy<P1...>, T, P2...>
{
    using type = typename internal<Obj, dummy<P1..., T>, P2...>::type;
};

template <template <typename ...> class Obj, typename ...P1, typename T, typename L>
struct internal<Obj, dummy<P1...>, T, L>
{
    using type = Obj<P1..., T>;
};

template <template <typename ...> class T, typename ...P>
struct subst_all_but_last
{
    using type = typename internal<T, dummy<>, P...>::type;
};

Use like this:

using bar = typename subst_all_but_last<some_template, Ts...>::type;

instead of

using bar = some_template<magically_get_all_but_last(Ts)...>;
Emile Cormier
  • 28,391
  • 15
  • 94
  • 122
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
0

This seems to work:

#include <tuple>
#include <utility>
#include <cstdint>

using std::size_t;
using std::tuple;

namespace 
{
    template <class Tuple, template <class...> class Template> struct apply_tuple_params_impl {};
    template <template <class...> class Template, class... Params>
    struct apply_tuple_params_impl<tuple<Params...>, Template>
    {
        typedef Template<Params...> type;
    };

    template <class Param, class Tuple> struct extend_tuple;
    template <class Param, class... Params> struct extend_tuple<Param, tuple<Params...>>
    {
        typedef tuple<Param, Params...> type;
    };

    template <class Param, class... Params> struct all_but_last_type_tuple_impl
    {
        typedef typename extend_tuple<Param, typename all_but_last_type_tuple_impl<Params...>::type>::type type;
    };

    template <class Param> struct all_but_last_type_tuple_impl<Param>
    {
        typedef tuple<> type;
    };
}

// pass the template parameters of a tuple to another template
template <class Tuple, template <class...> class Template>
using apply_tuple_params = typename apply_tuple_params_impl<Tuple, Template>::type;

// a tuple type with all the parameters except the last
template <class... Params>
using all_but_last_type_tuple = typename all_but_last_type_tuple_impl<Params...>::type;

And use it like this:

template <class... Ts> struct some_template;

template <class... Ts> struct foo
{
    typedef apply_tuple_params<all_but_last_type_tuple<Ts...>, some_template> bar;
};

static_assert(std::is_same<foo<int, bool, std::string>::bar, some_template<int,bool> >::value, "");
Tobias Brandt
  • 3,393
  • 18
  • 28