I am trying to implement a constexpr function "add42" that would allow me to do:
constexpr array<int,5> arr = {1,2,3,4,5};
constexpr array<int,5> arr2 = add42(0,arr); //I want arr2=={43,2,3,4,5}
That is, add an integer at a given index to an array statically (with constexpr).
Since my array "arr" is immutable, I have to actually create a new one from "arr" and my index. That is why I coded this function:
template<int DIM, typename... ARGS> auto constexpr
add42(int index, array<int,DIM> intergerArray, ARGS... unpackedIntegers) -> array<int,DIM> {
return
( sizeof...(ARGS)==DIM ) ?
array<int,DIM>( {{unpackedIntegers...}} ) :
( (sizeof...(ARGS)-1)==index ) ?
add42(index, intergerArray, unpackedIntegers..., intergerArray[sizeof...(ARGS)-1]+42 ) :
add42(index, intergerArray, unpackedIntegers..., intergerArray[sizeof...(ARGS)-1] ) ;
}
That is, all the integers of my array are recursively unpacked from the array, added 42 if at the right index, and appened at the end of the ARGS list. When this arg list contains all the integers from the array, we are done so we can repack into a new array.
However I get this error (gcc 4.7.2)
error: no matching function for call to 'add42(int, const std::array<int, 5u>&)'|
note: candidate is:|
template<int DIM, class ... ARGS> constexpr std::array<int, DIM> add42(int, std::array<int, DIM>, ARGS ...)|
note: template argument deduction/substitution failed:|
note: mismatched types 'int' and '#'integer_cst' not supported by dump_type#<type error>'|
Can you explain me what is the problem and how to correct it ?
This question seems similar to C++11: Compile Time Calculation of Array but is not (At least, I am unable to figure out how to use it deirectly): here, I want to create an new array from an already existent one, not from a known sequence of integers.
EDIT
Now I get infinite instantiation, even when no recursion called. Here is a simplified example :
template<size_t DIM, typename... ARGS> auto constexpr
add42(int index, array<int,DIM> integerArray, ARGS... unpackedIntegers) -> array<int,DIM> {
return
( true ) ?
array<int,DIM>( {{unpackedIntegers...}} ) :
add42(index, integerArray, unpackedIntegers..., integerArray[(sizeof...(ARGS)-1)] ) ;
}
Why does my compiler try to compile the last function call ?
EDIT 2
Apparently, I have to provide 2 function in order not to confuse the compiler:
template<size_t DIM, class... ARGS> constexpr auto
add42(int index, array<int,DIM> integerArray, ARGS... unpackedIntegers) -> typename enable_if<sizeof...(ARGS)==DIM ,array<int,DIM>>::type
{
return array<int,DIM>( {{unpackedIntegers...}} );
}
template<size_t DIM, class... ARGS> constexpr auto
add42(int index, array<int,DIM> integerArray, ARGS... unpackedIntegers) -> typename enable_if<sizeof...(ARGS)!=DIM ,array<int,DIM>>::type
{
return
( sizeof...(ARGS) == index ) ?
add42(index, integerArray, unpackedIntegers..., integerArray[sizeof...(ARGS)]+42) :
add42(index, integerArray, unpackedIntegers..., integerArray[sizeof...(ARGS)]) ;
}
But it still does not work:
recursively required from [[name of the second function]]
Apparently, a variadic function cannot call "recursively" one of its overloads.
Am I right ? What workaround is possible ?
You should use
template<size_t DIM, typename... ARGS> auto constexpr
add42(int index, array<int,DIM> intergerArray, ARGS... unpackedIntegers)
since array's second parameter has type size_t, not int.
Unfortunately, std::array::operator[] is not constexpr.
Compiler options: just plain -std=c++11
gcc <= 4.6: -std=c++0x and replace the using directive with a typedef
#include <cstddef>
//#include <array>
#include <type_traits>
#include <iostream>
template < typename T, std::size_t dim >
struct c_array
{
T arr[dim];
constexpr T operator[](std::size_t index)
{ return arr[index]; }
T const* begin() const
{ return arr; }
T const* end() const
{ return arr+dim; }
};
// I like the overloaded version better (instead of enable_if) :)
template < typename T, std::size_t dim, typename... TT >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, std::size_t index, std::true_type, TT... pp)
{
return {{pp...}};
}
template < typename T, std::size_t dim, typename... TT >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, std::size_t index, std::false_type, TT... pp)
{
using test = std::integral_constant<bool, (sizeof...(pp)+1 == dim)>;
return index == sizeof...(pp)
? add_to(s, in, index, test{}, pp..., in[sizeof...(pp)]+s)
: add_to(s, in, index, test{}, pp..., in[sizeof...(pp)] );
}
// unfortunately, I don't know how to avoid this additional overload :(
template < typename T, std::size_t dim>
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, std::size_t index)
{
return add_to(s, in, index, std::false_type{});
}
constexpr c_array<int,5> arr = {1,2,3,4,5};
constexpr c_array<int,5> arr2 = add_to(42, arr, 0); //I want arr2=={43,2,3,4,5}
int main()
{
for(auto const& e : arr2)
{
std::cout << e << ", ";
}
}
Alternative version, slightly awkward usage syntax:
// helper; construct a sequence of non-type template arguments
template < std::size_t... tt_i >
struct seq
{};
template < std::size_t t_n, std::size_t... tt_i >
struct gen_seq
: gen_seq < t_n-1, t_n-1, tt_i...>
{};
template < std::size_t... tt_i >
struct gen_seq < 0, tt_i... >
: seq < tt_i... >
{};
template < std::size_t index, typename T, std::size_t dim,
std::size_t... tt_bef, std::size_t... tt_aft >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in, seq<tt_bef...>, seq<tt_aft...>)
{
return {{ in[tt_bef]..., in[index]+s, in[tt_aft]... }};
}
template < std::size_t index, typename T, std::size_t dim >
constexpr c_array<T, dim>
add_to(T s, c_array<T, dim> in)
{
return add_to<index>(s, in, gen_seq<index>{}, gen_seq<dim-index-1>{});
}
constexpr c_array<int,5> arr = {1,2,3,4,5};
constexpr c_array<int,5> arr2 = add_to<0>(42, arr);
Related
I'm having trouble unrolling and forwarding a parameter pack of std::arrays to another function
Suppose we have a function that takes a single std::array and I want to unroll it and pass it as an argument to some other function I can do so by doing this:
template<typename T, typename...Ts>
void other_function(T, Ts...) { /* Do stuff with Ts */ }
template<typename T, size_t Size, size_t... I>
void forward_array(std::array<T, Size>& array_arg, std::index_sequence<I...>)
{
other_function(array_arg[I]...);
// for Size == 3 let's say we get the same thing as we would write
// other_function(array_arg[0], array_arg[1], array_arg[2]
// I skipped the std::forward
}
Now let's say we have a function that does this same thing, but it takes multiple arrays that can be of different size.
template<typename T, size_t... Sizes /*, size_t... I_Sequence0, size_t... I_Sequence1, ... I_SequenceN */>
void forward_many_arrays(std::array<T, Sizes>&... array_args /*, ???*/)
{
other_func( /* ??? */);
}
I want to unfold each array_arg and pass it to other_func, but how do I do that exactly here?. We would need a way to index into each array arg.
In my actual program, I have a class that has a member std::array of std::reference_wrapper which is not default constructable and I'm trying to provide an alternative constructor for that class that takes any number of arrays&, where the sum of their sizes matches the member array size and delegate it to the explicit constructor that takes T references, but I'm kind of stuck cause I don't know how to handle the unrolling.
You might have a "generic" getter
template <std::size_t I, typename Container, typename ... Containers>
decltype(auto) get(Container&& container, Containers&&...containers)
{
constexpr std::size_t size = std::tuple_size_v<std::decay_t<Container>>;
if constexpr (I < size) {
return container[I];
} else {
return get<I - size>(containers...);
}
}
Used like:
template <typename...Ts>
void other_function(Ts... ts) { ((std::cout << ts << " "), ...); }
template<typename... Ts, size_t... Is>
void forward_many_arrays(std::index_sequence<Is...>, Ts&&...ts)
{
other_function(get<Is>(ts...)...);
}
template<typename... Ts>
void forward_many_arrays(Ts&&...ts)
{
forward_many_arrays(std::make_index_sequence<(std::tuple_size_v<std::decay_t<Ts>> + ...)>(), ts...);
}
Demo
An implementation based on simple recursion:
template<std::size_t n, class Fn, class T, class... Ts>
void apply_all_impl(Fn fn, T& t, Ts&... ts) {
if constexpr (n == 0)
fn(t, ts...);
else
std::apply([&](auto&... args) {
apply_all_impl<n - 1>(fn, ts..., args...);
}, t);
}
template<class Fn, class... Ts>
void apply_all(Fn fn, Ts&... ts) {
apply_all_impl<sizeof...(Ts)>(fn, ts...);
}
Usage example:
std::array<int, 3> arr1{1, 2, 3};
std::array<int, 4> arr2{4, 5, 6, 7};
auto print_all = [](auto... ts) { (std::cout << ... << ts); };
apply_all(print_all, arr1, arr2); // Output: 1234567
Demo
I have implemented a simple fold function in C++ that accepts a lambda, and can fold multiple vectors at the same time at compile time. I am wondering if it could be simplified in some manner (I have provided both a recursive version and an iteratively recursive version - I am unsure which should have better performance): https://godbolt.org/z/39pW81
Performance optimizations are also welcome - in that regard is any of the two approaches faster?
template<int I, typename type_identity, typename type_head, int N, typename ...type_tail, int ...N_tail, typename Function>
auto foldHelperR(Function&& func, const type_identity& id, const tvecn<type_head, N>& head, const tvecn<type_tail, N_tail>&... tail)
{
if constexpr (I>0)
{
return func(foldHelperR<I-1>(std::forward<Function>(func), id, head, tail...), head[I], tail[I]...);
}
else
{
return func(id, head[0], tail[0]...);
}
}
template<int I, typename type_identity, typename type_head, int N, typename ...type_tail, int ...N_tail, typename Function>
auto foldHelperI(Function&& func, const type_identity id, const tvecn<type_head, N>& head, const tvecn<type_tail, N_tail>&... tail)
{
if constexpr (I<N-1)
{
return foldHelperI<I+1>(std::forward<Function>(func), func(id, head[I], tail[I]...), head, tail...);
}
else
{
return func(id, head[N-1], tail[N-1]...);
}
}
template<typename type_identity, typename type_head, int N_head, typename ...type_tail, int ...N_tail, typename Function = void (const type_identity&, const type_head&, const type_tail&...)>
constexpr auto fold(Function&& func, const type_identity& id, const tvecn<type_head, N_head>& head, const tvecn<type_tail, N_tail>&... tail)
{
static_assert(std::is_invocable_v<Function, const type_identity&, const type_head&, const type_tail &...>,
"The function cannot be invoked with these zip arguments (possibly wrong argument count).");
static_assert(all_equal_v<N_head, N_tail...>, "Vector sizes must match.");
//return foldHelperR<N_head-1>(std::forward<Function>(func), id, head, tail...);
return foldHelperI<0>(std::forward<Function>(func), id, head, tail...);
}
int main()
{
tvecn<int,3> a(1,2,3);
return fold([](auto x, auto y, auto z) {return x+y+z;}, 0, a, a);
}
and can fold multiple vectors at the same time at compile time
Not exactly: if you want to operate compile-time
(1) you have to define constexpr the tvecn constructor and
(2) you have to define constexpr the foldhelper function and
(3) you have to declare constexpr a
// VVVVVVVVV
constexpr tvecn<int,3> a(1,2,3);
(4) you have to place the result of fold in a constexpr variable (or, more generally speaking, in a place where the value is required compile time, as the size field of a C-style array, or a template value parameter, or a static_assert() test)
constexpr auto f = fold([](auto x, auto y, auto z) {return x+y+z;},
0, a, a);
I am wondering if it could be simplified in some manner
Sure.
First of all: if you can, avoid to reinventing the weel: your tvecn is a simplified version of std::array.
Suggestion: use std::array (if you can obviously)
Second: you tagged C++17 so you can use folding
Suggestion: use it also for all_equal
template <auto V0, auto ... Vs>
struct all_equal : public std::bool_constant<((V0 == Vs) && ...)>
{ };
template<auto ...N_pack>
constexpr bool all_equal_v = all_equal<N_pack...>::value;
More in general: when you have to define a custom type traits that has to provide a number, inherit (if possible) from std::integral_constant (or std::bool_constant, or std::true_type, or std::false_type: all std::integral_constant specializations). So you automatically inherit all std::integral_constant facilities.
Third: almost all C++ standard uses std::size_t, not int, for sizes.
Suggestion: when you have to do with sizes, use std::size_t, not int. This way you can avoid a lot of annoying troubles.
Fourth: from main() you should return only EXIT_SUCCESS (usually zero) or EXIT_FAILURE (usually 1)
Suggestion: avoid things as
return fold([](auto x, auto y, auto z) {return x+y+z;}, 0, a, a);
Fifth: never underestimate the power of the comma operator.
Suggestion: avoid recursion at all and use template folding also for the helper function; by example
template <std::size_t ... Is, typename F, typename T, typename ... As>
constexpr auto foldHelperF (std::index_sequence<Is...>,
F const & f, T id, As const & ... arrs)
{ return ( ..., (id = [&](auto i){ return f(id, arrs[i]...); }(Is))); }
that you can call as follows from fold()
return foldHelperF(std::make_index_sequence<N_head>{},
std::forward<Function>(func),
id, head, tail...);
The following is a full compiling, and simplified, example
#include <array>
#include <utility>
#include <iostream>
#include <type_traits>
template <auto V0, auto ... Vs>
struct all_equal : public std::bool_constant<((V0 == Vs) && ...)>
{ };
template<auto ...N_pack>
constexpr bool all_equal_v = all_equal<N_pack...>::value;
template <std::size_t ... Is, typename F, typename T, typename ... As>
constexpr auto foldHelperF (std::index_sequence<Is...>,
F const & f, T id, As const & ... arrs)
{ return ( ..., (id = [&](auto i){ return f(id, arrs[i]...); }(Is))); }
template <typename type_identity, typename type_head, std::size_t N_head,
typename ...type_tail, std::size_t ...N_tail,
typename Function = void (type_identity const &,
type_head const &,
type_tail const & ...)>
constexpr auto fold (Function && func, type_identity const & id,
std::array<type_head, N_head> const & head,
std::array<type_tail, N_tail> const & ... tail)
{
static_assert( std::is_invocable_v<Function, const type_identity&,
const type_head&, const type_tail &...>,
"The function cannot be invoked with these zip arguments"
" (possibly wrong argument count).");
static_assert( all_equal_v<N_head, N_tail...>,
"Vector sizes must match.");
return foldHelperF(std::make_index_sequence<N_head>{},
std::forward<Function>(func),
id, head, tail...);
}
int main()
{
constexpr std::array<int, 3u> b{2, 5, 7};
constexpr auto f = fold([](auto x, auto y, auto z) {return x+y+z;},
0, b, b);
std::cout << f << std::endl;
}
With Fold expression, it might be:
template <typename F, typename Init, std::size_t... Is, typename... Arrays>
constexpr auto fold_impl(F&& f, Init init, std::index_sequence<Is...>, Arrays&&... arrays)
{
auto l = [&](Init init, std::size_t i){ return f(init, arrays[i]...); };
return ((init = l(init, Is)), ...);
}
template <typename F, typename Init, typename Array, typename ... Arrays>
constexpr auto fold(F&& f, Init init, Array&& array, Arrays&&... arrays)
{
static_assert(((arrays.size() == array.size()) && ...));
return fold_impl(f, init, std::make_index_sequence<array.size()>{}, array, arrays...);
}
Demo
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?
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.
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.
To start with, this is basically asking to concatenate an arbitrary number of arrays. Which is very similar to concatenate an arbitrary number of tuples, for which there is a standard library function, even in C++11: std::tuple_cat(). That gets us almost there:
template <class... Ts, class M>
auto get_array(M m) -> decltype(std::tuple_cat(m.template get<Ts>()...)) {
return std::tuple_cat(m.template get<Ts>()...);
}
Note that I flipped the template parameters, so this is just get_array<T1, T2>(m) instead of having to write get_array<M, T1, T2>(m).
Now the question is, how do we write array_cat? We'll just use tuple_cat and convert the resulting tuple to an array. Assume an implementation of index_sequence is available (which is something you'll want in your collection anyway):
template <class T, class... Ts, size_t... Is>
std::array<T, sizeof...(Ts)+1> to_array_impl(std::tuple<T, Ts...>&& tup,
std::index_sequence<Is...> ) {
return {{std::get<Is>(std::move(tup))...}};
}
template <class T, class... Ts>
std::array<T, sizeof...(Ts)+1> to_array(std::tuple<T, Ts...>&& tup) {
return to_array_impl(std::move(tup), std::index_sequence_for<T, Ts...>());
}
template <class... Tuples>
auto array_cat(Tuples&&... tuples) -> decltype(to_array(std::tuple_cat(std::forward<Tuples>(tuples)...))) {
return to_array(std::tuple_cat(std::forward<Tuples>(tuples)...));
}
And that gives you:
template <class... Ts, class M>
auto get_array(M m) -> decltype(array_cat(m.template get<Ts>()...)) {
return array_cat(m.template get<Ts>()...);
}
which handles arbitrarily many types.
So here's for an arbitrary number of same-type arrays. We are basically implementing a highly restrictive version of tuple_cat, made substantially easier because the number of elements in the arrays is the same. I make use of a couple C++14 and 17 library features that are all readily implementable in C++11.
template<class, size_t> struct div_sequence;
template<size_t...Is, size_t Divisor>
struct div_sequence<std::index_sequence<Is...>, Divisor>
{
using quot = std::index_sequence<Is / Divisor...>;
using rem = std::index_sequence<Is % Divisor...>;
};
template<class T, size_t...Ns, size_t...Is, class ToA>
std::array<T, sizeof...(Ns)> array_cat_impl(std::index_sequence<Ns...>,
std::index_sequence<Is...>,
ToA&& t)
{
// NB: get gives you perfect forwarding; [] doesn't.
return {std::get<Is>(std::get<Ns>(std::forward<ToA>(t)))... };
}
template<class Array, class... Arrays,
class VT = typename std::decay_t<Array>::value_type,
size_t S = std::tuple_size<std::decay_t<Array>>::value,
size_t N = S * (1 + sizeof...(Arrays))>
std::array<VT, N> array_cat(Array&& a1, Arrays&&... as)
{
static_assert(std::conjunction_v<std::is_same<std::decay_t<Array>,
std::decay_t<Arrays>>...
>, "Array type mismatch");
using ind_seq = typename div_sequence<std::make_index_sequence<N>, S>::rem;
using arr_seq = typename div_sequence<std::make_index_sequence<N>, S>::quot;
return array_cat_impl<VT>(arr_seq(), ind_seq(),
std::forward_as_tuple(std::forward<Array>(a1),
std::forward<Arrays>(as)...)
);
}
We can also reuse the tuple_cat machinery, as in #Barry's answer. To sidestep potential QoI issues, avoid depending on extensions and also extra moves, we don't want to tuple_cat std::arrays directly. Instead, we transform the array into a tuple of references first.
template<class TupleLike, size_t... Is>
auto as_tuple_ref(TupleLike&& t, std::index_sequence<Is...>)
-> decltype(std::forward_as_tuple(std::get<Is>(std::forward<TupleLike>(t))...))
{
return std::forward_as_tuple(std::get<Is>(std::forward<TupleLike>(t))...);
}
template<class TupleLike,
size_t S = std::tuple_size<std::decay_t<TupleLike>>::value >
auto as_tuple_ref(TupleLike&& t)
-> decltype(as_tuple_ref(std::forward<TupleLike>(t), std::make_index_sequence<S>()))
{
return as_tuple_ref(std::forward<TupleLike>(t), std::make_index_sequence<S>());
}
We can then transform the tuple_cat'd references back into an array:
template <class R1, class...Rs, size_t... Is>
std::array<std::decay_t<R1>, sizeof...(Is)>
to_array(std::tuple<R1, Rs...> t, std::index_sequence<Is...>)
{
return { std::get<Is>(std::move(t))... };
}
template <class R1, class...Rs>
std::array<std::decay_t<R1>, sizeof...(Rs) + 1> to_array(std::tuple<R1, Rs...> t)
{
static_assert(std::conjunction_v<std::is_same<std::decay_t<R1>, std::decay_t<Rs>>...>,
"Array element type mismatch");
return to_array(t, std::make_index_sequence<sizeof...(Rs) + 1>());
}
Finally, array_cat itself is just
template <class... Arrays>
auto array_cat(Arrays&&... arrays)
-> decltype(to_array(std::tuple_cat(as_tuple_ref(std::forward<Arrays>(arrays))...)))
{
return to_array(std::tuple_cat(as_tuple_ref(std::forward<Arrays>(arrays))...));
}
Any decent optimizer should have little difficulty optimizing the intermediate tuples of references away.
I have a variadic template function foo():
template <typename... Args>
void foo(Args &&... args);
This function is intended to be invoked with all arguments of size_t. I can enforce that using some metaprogramming. I need to take the resulting list of arguments two at a time and put them into a container of std::pair<size_t, size_t>. Conceptually, something like:
std::vector<std::pair<size_t, size_t> > = {
std::make_pair(args[0], args[1]),
std::make_pair(args[2], args[3]), ...
};
Is there a straightforward way to do this? I know that by pack expansion, I could put the arguments into a flat container, but is there a way to group them two by two into std::pair objects at the same time?
Indexing into packs isn't really doable (yet?), but indexing into tuples is. Just stick everything into a tuple first, and then pull everything back out as you go. Since everything's a size_t, we can just copy:
template <size_t... Is, class Tuple>
std::vector<std::pair<size_t, size_t>>
foo_impl(std::index_sequence<Is...>, Tuple tuple) {
return std::vector<std::pair<size_t, size_t> >{
std::make_pair(std::get<2*Is>(tuple), std::get<2*Is+1>(tuple))...
};
}
template <typename... Args>
void foo(Args... args)
{
auto vs = foo_impl(std::make_index_sequence<sizeof...(Args)/2>{},
std::make_tuple(args...));
// ...
}
Suppose you are allowed to refactor your logic into an internal helper function:
template <typename ...Args>
void foo(Args &&... args)
{
foo_impl(std::make_index_sequence<sizeof...(Args) / 2>(),
std::forward<Args>(args)...);
}
Now we can operate on the argument pack index by index:
template <std::size_t ...I, typename ...Args>
void foo_impl(std::index_sequence<I...>, Args &&... args)
{
std::vector<std::pair<std::size_t, std::size_t>> v =
{ GetPair(std::integral_constant<std::size_t, I>(), args...)... };
}
It remains to implement the pair extractor:
template <typename A, typename B, typename ...Tail>
std::pair<std::size_t, std::size_t> GetPair(std::integral_constant<std::size_t, 0>,
A a, B b, Tail ... tail)
{
return { a, b };
}
template <std::size_t I, typename A, typename B, typename ...Tail>
std::pair<std::size_t, std::size_t> GetPair(std::integral_constant<std::size_t, I>,
A a, B b, Tail ... tail)
{
return GetPair<I - 1>(tail...);
}
With range-v3, you may do
template <typename... Args>
void foo(Args&&... args)
{
std::initializer_list<std::size_t> nbs = {static_cast<std::size_t>(args)...};
const auto pair_view =
ranges::view::zip(nbs | ranges::view::stride(2),
nbs | ranges::view::drop(1) | ranges::view::stride(2));
// And possibly
std::vector<std::pair<std::size_t, std::size_t>> pairs = pair_view;
// ...
}
Demo
Someone (cough #Barry cough) said that indexing into packs isn't possible.
This is C++. Impossible means we just haven't written it yet.
template<std::size_t I> struct index_t:std::integral_constant<std::size_t, I> {
using std::integral_constant<std::size_t, I>::integral_constant;
template<std::size_t J>
constexpr index_t<I+J> operator+( index_t<J> ) const { return {}; }
template<std::size_t J>
constexpr index_t<I-J> operator-( index_t<J> ) const { return {}; }
template<std::size_t J>
constexpr index_t<I*J> operator*( index_t<J> ) const { return {}; }
template<std::size_t J>
constexpr index_t<I/J> operator/( index_t<J> ) const { return {}; }
};
template<std::size_t I>
constexpr index_t<I> index{};
template<std::size_t B>
constexpr index_t<1> exponent( index_t<B>, index_t<0> ) { return {}; }
template<std::size_t B, std::size_t E>
constexpr auto exponent( index_t<B>, index_t<E> ) {
return index<B> * exponent( index<B>, index<E-1> );
}
template<std::size_t N>
constexpr index_t<0> from_base(index_t<N>) { return {}; }
template<std::size_t N, std::size_t c>
constexpr index_t<c-'0'> from_base(index_t<N>, index_t<c>) { return {}; }
template<std::size_t N, std::size_t c0, std::size_t...cs>
constexpr auto from_base(index_t<N>, index_t<c0>, index_t<cs>...) {
return
from_base(index<N>, index<c0>) * exponent(index<N>, index<sizeof...(cs)>)
+ from_base(index<N>, index<cs>...)
;
}
template<char...cs>
constexpr auto operator""_idx(){
return from_base(index<10>, index<cs>...);
}
auto nth = [](auto index_in){
return [](auto&&...elems)->decltype(auto){
using std::get;
constexpr auto I= index<decltype(index_in){}>;
return get<I>(std::forward_as_tuple(decltype(elems)(elems)...));
};
};
Now we get:
using pair_vec = std::vector<std::pair<std::size_t, std::size_t>>;
template <typename... Args>
pair_vec foo(Args &&... args) {
return
index_over< sizeof...(args)/2 >()
([&](auto...Is)->pair_vec{
return {
{
nth( Is*2_idx )( decltype(args)(args)... ),
nth( Is*2_idx+1_idx )( decltype(args)(args)... )
}...
};
});
}
where we "directly" index into our parameter packs using compile time constant indexes.
live example.
Is there a better way more compact to implement the computation of nBits below at compile time? Please note that the question is not how to implement n_active_bits, which I know how to do.
constexpr int n_active_bits(int m) { /* count the bits */ }
template <uint8_t...Masks>
struct MaskPack
{
// is there a more concise way than to implement
//the auxiliary recursive function count_bits?
static constexpr uint8_t nBits = count_bits(Masks...);
private:
template <typename M, typename...Ms>
static constexpr uint8_t count_bits(M&& m, Ms&&...ms)
{
return n_active_bits(m) + count_bits(ms...);
}
static constexpr uint8_t count_bits()
{
return 0;
}
};
I tried with no luck to use the one-liner C++17 folds:
static constexpr uint8_t nBits = (n_active_bits(Masks) + ...);
With folds you can do it as follows:
template <uint8_t...Masks>
struct MaskPack {
static constexpr uint8_t nBits = (n_active_bits(Masks) + ...);
};
Live Demo
Note that n_active_bits must be constexpr:
constexpr int n_active_bits(unsigned int mask) {
return mask == 0 ? 0 : (mask & 0x01) + n_active_bits(mask >> 1);
}
It requires some boilerplate.
template<size_t I, class Indexes>
struct offset_indexes;
template<size_t I, class Indexes>
using offset_indexes_t = typename offset_indexes<I,Indexes>::type;
template<size_t I, size_t...Is>
struct offset_indexes<I, std::index_sequence<Is...>> {
using type=std::index_sequence<(I+Is)...>;
};
then a binary fold:
template<size_t...Is, class Tuple>
constexpr auto retuple( std::index_sequence<Is...>, Tuple&& tuple ) {
return std::forward_as_tuple( std::get<Is>(std::forward<Tuple>(tuple))... );
}
template<class F, class T>
constexpr T binary_fold( F&& f, std::tuple<T>&& tuple ) {
return std::get<0>(std::move(tuple));
}
template<class Tuple>
struct first_element {};
template<class Tuple>
using first_element_t=typename first_element<Tuple>::type;
template<template<class...>class Z, class T0, class...Ts>
struct first_element<Z<T0,Ts...>>{using type=T0;};
template<class F, class Tuple, class E=first_element_t<std::decay_t<Tuple>>>
constexpr std::decay_t<E> binary_fold( F&& f, Tuple&& tuple ) {
constexpr auto count = std::tuple_size<std::decay_t<Tuple>>{};
using first_half = std::make_index_sequence< count/2 >;
using second_half = offset_indexes_t<
count/2,
std::make_index_sequence< (count+1)/2 >
>;
return f(
binary_fold( f, retuple( first_half{}, std::forward<Tuple>(tuple) ) ),
binary_fold( f, retuple( second_half{}, std::forward<Tuple>(tuple) ) )
);
}
which should make a binary tree of recursive calls to binary_fold.
All of this makes count_bits easy:
template <class...Ms>
constexpr size_t count_bits(Ms&&...ms)
{
return binary_fold(
std::plus<>{},
std::make_tuple(n_active_bits(std::forward<Ms>(ms))...)
);
}
note, however, that there is lots of boilerplate.
I did a more complex binary fold, because left/right fold has a deep recursive depth.
live example.