Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Expand two parameter packs

Tags:

c++

c++11

Consider following piece of code:

static constexpr size_t Num {2};
struct S {
    std::array<size_t, Num> get () { return {1, 2}; }
};

struct S1 : S {};
struct S2 : S {};

struct M {
    template <typename T>
    typename std::enable_if<std::is_same<T, S1>::value, S1>::type get () const { 
        return S1 {}; 
    }

    template <typename T>
    typename std::enable_if<std::is_same<T, S2>::value, S2>::type get () const { 
        return S2 {}; 
    }
};

I want to have a function which merges two or more std::arrays making one std::array.

So far I ended with something like this:

template <typename Mode, typename... Rs, size_t... Ns>
std::array<size_t, sizeof... (Rs)*Num> get_array (const Mode& mode, Sequence::Sequence<Ns...>) {
    return {std::get<Ns> (mode.template get<Rs...> ().get ())...};
}

I want to have that the following code

M m;
auto x = get_array<M, S1, S2> (m, Sequence::Make<2> {});

produces std::array<size_t, 4> filled with {1, 2, 1, 2}.

Where Sequence::Sequence and Sequence::Make are described here.

I know that placing ... of Rs is incorrect in this context (If sizeof... (Rs) is 1 then it is fine, std::array<size_t, 2> with {1, 2} is returned) but I have no idea where to put it to make expansion which looks like this:

std::get<0> (mode.template get<Rs[0]> ().get ()), 
std::get<1> (mode.template get<Rs[0]> ().get ()),
std::get<0> (mode.template get<Rs[1]> ().get ()), 
std::get<1> (mode.template get<Rs[1]> ().get ());

Of course Rs[0] I mean first type from parameter pack.

Is it even possible?

like image 830
Artur Pyszczuk Avatar asked Sep 22 '17 12:09

Artur Pyszczuk


People also ask

What is parameter pack in c++?

Parameter packs (C++11) A parameter pack can be a type of parameter for templates. Unlike previous parameters, which can only bind to a single argument, a parameter pack can pack multiple parameters into a single parameter by placing an ellipsis to the left of the parameter name.

What is Variadic template in C++?

Variadic templates are class or function templates, that can take any variable(zero or more) number of arguments. In C++, templates can have a fixed number of parameters only that have to be specified at the time of declaration.


2 Answers

Assuming that we're using Xeo's index sequence implementation, we can do something like this:

First create a function for concatenating two arrays. It receives the arrays, plus an index sequence for each one (detail::seq is the index_sequence type)

template<class T, size_t N, size_t M, size_t... I, size_t... J>
std::array<T, N + M> concat(const std::array<T, N>& arr1, const std::array<T, M>& arr2, detail::seq<I...>, detail::seq<J...>)
{
     return {arr1[I]..., arr2[J]...};
}

Next, call this function from your get_array function, except we're going to double the seq that we received from the call in main:

template<class MODE, class... T, size_t... I>
auto get_array(MODE m, detail::seq<I...>) ->decltype(concat(m.template get<T>().get()..., detail::seq<I...>{}, detail::seq<I...>{})){
    return concat(m.template get<T>().get()..., detail::seq<I...>{}, detail::seq<I...>{});
}

The call in main looks just like it did in your code:

M m;
auto x = get_array<M, S1, S2>(m, detail::gen_seq<2>{});

Where detail::gen_seq is the implementation of make_index_sequence that Xeo had.

Live Demo

Note that I replaced unsigned with size_t in Xeo's index sequence impl.

In C++14 we don't need to implement seq or gen_seq, and we also wouldn't need a trailing -> decltype() after our function.

In C++17 it would be even easier to generalize our concatenation for an arbitrary number of arrays, using fold expressions.

like image 74
AndyG Avatar answered Oct 15 '22 15:10

AndyG


Yes, this can be done, with the standard index_sequence tricks:

template <class T, std::size_t N1, std::size_t N2, std::size_t ... Is, std::size_t ... Js>
std::array<T, N1 + N2> merge_impl(const std::array<T, N1>& a1,
                                  const std::array<T, N2>& a2, 
                                  std::index_sequence<Is...>,
                                  std::index_sequence<Js...>) {
    return {a1[Is]..., a2[Js]...};
}

template <class T, std::size_t N1, std::size_t N2>
std::array<T, N1 + N2> merge(const std::array<T, N1>& a1, const std::array<T, N2>& a2) {
    return merge_impl(a1, a2,
                      std::make_index_sequence<N1>{}, 
                      std::make_index_sequence<N2>{});
}

index_sequence is only in the 14 standard, but can be easily implemented in 11; there are many resources (including on SO) that describe how to do so (edit: it's basically equivalent to your Sequence stuff, may as well get used to the standard names for them). Live example: http://coliru.stacked-crooked.com/a/54dce4a695357359.

like image 3
Nir Friedman Avatar answered Oct 15 '22 15:10

Nir Friedman