Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 482
abraham_hilbert Avatar asked Oct 18 '16 15:10

abraham_hilbert


3 Answers

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{};
}
like image 103
skypjack Avatar answered Sep 30 '22 11:09

skypjack


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>());
}
like image 24
Vaughn Cato Avatar answered Sep 30 '22 11:09

Vaughn Cato


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<>{})
        );
};        
like image 9
Barry Avatar answered Sep 30 '22 11:09

Barry