abraham_hilbert
abraham_hilbert

Reputation: 2271

Get first N elements of parameter pack

I have to following problem:

template< size_t... N_i >
class A
{
  // ...
};

template< size_t N, size_t... N_i >
A</* first N elements of N_i...*/> foo()
{
  A</* first N elements of N_i...*/> a;

  // ...

  return a;
}

int main()
{
  A<1,2> res = foo<2, 1,2,3,4>();

  return 0;
}

Here, I want foo to have the return type A</* first N size_t of N_i...*/>, i.e., the class A which has as template arguments the first N elements of the parameter pack N_i.

Does anyone know how this can be implemented?

Upvotes: 25

Views: 7895

Answers (6)

W.F.
W.F.

Reputation: 13988

You could also make use of variadic generic lambda expression and reusable helper structure to perform compile-time iteration:

#include <utility>
#include <tuple>

template <std::size_t N, class = std::make_index_sequence<N>>
struct iterate;

template <std::size_t N, std::size_t... Is>
struct iterate<N, std::index_sequence<Is...>> {
   template <class Lambda>
   auto operator()(Lambda lambda) {
      return lambda(std::integral_constant<std::size_t, Is>{}...);
   }
};

template <size_t... Is>
struct A { };

template <size_t N, size_t... Is>
auto foo() {
   return iterate<N>{}([](auto... ps){
      using type = std::tuple<std::integral_constant<std::size_t, Is>...>;
      return A<std::tuple_element_t<ps, type>{}...>{};
   });
}

int main() {
   decltype(foo<3, 1, 2, 3, 4>()) a; // == A<1, 2, 3> a;
}

Upvotes: 3

Vaughn Cato
Vaughn Cato

Reputation: 64308

This is a slight variation on @skypjack's answer that avoids using tuples:

template <size_t... N_i,size_t... M_i>
auto foo2(std::index_sequence<M_i...>)
{
    constexpr size_t values[] = {N_i...};
    return A<values[M_i]...>();
}

template <size_t N,size_t... N_i>
auto foo()
{
    return foo2<N_i...>(std::make_index_sequence<N>());
}

Upvotes: 10

skypjack
skypjack

Reputation: 50550

Here is the shortest solution that came to my mind (with two lines spent for an alias).
It follows a minimal, working example based on the code posted by the OP:

#include<functional>
#include<cstddef>
#include<utility>
#include<tuple>

template<std::size_t... V>
class A {};

template<std::size_t... V, std::size_t... I>
constexpr auto func(std::index_sequence<I...>) {
    return A<std::get<I>(std::make_tuple(V...))...>{};
}

template<std::size_t N, std::size_t... V>
constexpr auto func() {
    return func<V...>(std::make_index_sequence<N>{});
}

template<std::size_t N, std::size_t... V>
using my_a = decltype(func<N, V...>());

int main() {
    A<1,2> res1 = func<2, 1, 2, 3, 4>();
    // Or even better...
    decltype(func<2, 1, 2, 3, 4>()) res2{};
    // Or even better...
    my_a<2, 1, 2, 3, 4> res3{};
}

Upvotes: 17

Barry
Barry

Reputation: 303127

The most direct subproblem is in the land of typelists:

template <class... Ts>
struct typelist {
    using type = typelist;
    static constexpr std::size_t size = sizeof...(Ts);
};

template <class T>
struct tag { using type = T; };

template <std::size_t N, class TL>
struct head_n {
    using type = ???;
};

Now, head_n is just a matter of simple recursion - move an element from one list to another list N times starting from an empty list.

template <std::size_t N, class R, class TL>
struct head_n_impl;

// have at least one to pop from and need at least one more, so just 
// move it over
template <std::size_t N, class... Ts, class U, class... Us>
struct head_n_impl<N, typelist<Ts...>, typelist<U, Us...>>
: head_n_impl<N-1, typelist<Ts..., U>, typelist<Us...>>
{ };

// we have two base cases for 0 because we need to be more specialized
// than the previous case regardless of if we have any elements in the list
// left or not
template <class... Ts, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<Us...>>
: tag<typelist<Ts...>>
{ };

template <class... Ts, class U, class... Us>
struct head_n_impl<0, typelist<Ts...>, typelist<U, Us...>>
: tag<typelist<Ts...>>
{ };

template <std::size_t N, class TL>
using head_n = typename head_n_impl<N, typelist<>, TL>::type;

Going from this to your specific problem I leave as an exercise to the reader.


An alternate approach is via concatenation. Convert every element of a typelist<Ts...> into either a typelist<T> or a typelist<>, and then concat them all together. concat is straightforward:

template <class... Ts>
struct concat { };

template <class TL>
struct concat<TL>
: tag<TL>
{ };

template <class... As, class... Bs, class... Rest>
struct concat<typelist<As...>, typelist<Bs...>, Rest...>
: concat<typelist<As..., Bs...>, Rest...>
{ };

And then we can do:

template <std::size_t N, class TL, class = std::make_index_sequence<TL::size>>
struct head_n;

template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
: concat<
        std::conditional_t<(Is < N), typelist<Ts>, typelist<>>...
        >
{ };

template <std::size_t N, class TL>
using head_n_t = typename head_n<N, TL>::type;

The advantage of this latter approach is that concat can be replaced in C++17 by a fold-expression given an appropriate operator+:

template <class... As, class... Bs>
constexpr typelist<As..., Bs...> operator+(typelist<As...>, typelist<Bs...> ) {
    return {};
}

which allows:

template <std::size_t N, class... Ts, std::size_t... Is>
struct head_n<N, typelist<Ts...>, std::index_sequence<Is...>>
{
    using type = decltype(
        (std::conditional_t<(Is < N), typelist<Ts>, typelist<>>{} + ... + typelist<>{})
        );
};        

Upvotes: 9

krzaq
krzaq

Reputation: 16421

This is fairly simple with Boost.Hana:

namespace hana = boost::hana;

template<size_t... vals>
auto make_a(hana::tuple<hana::integral_constant<size_t, vals>...>)
{
    return A<vals...>{};
}

template<size_t N, size_t... vals>
auto foo(){
    constexpr auto front = hana::take_front(
        hana::tuple_c<size_t, vals...>,
        hana::integral_c<size_t,N>
    );
    return detail::make_a(front);
}

live demo

Upvotes: 4

Alex Aparin
Alex Aparin

Reputation: 4512

Unfortunately, such method requires to define additional Helper types

template< size_t... N_i >
class A
{
};

template <size_t... N_i>
struct Helper;

template <size_t... N_i>
struct Helper<0, N_i...>
{
    typedef A<> type;
};

template <size_t N0, size_t... N_i>
struct Helper<1, N0, N_i...>
{
    typedef A<N0> type;
};

template <size_t N0, size_t N1, size_t... N_i>
struct Helper<2, N0, N1, N_i...>
{
    typedef A<N0, N1> type;
};

template< size_t N, size_t... N_i >
typename Helper<N, N_i...>::type foo()
{
  typename Helper<N, N_i...>::type a;
  return a;
}

Upvotes: 2

Related Questions