`constexpr` reduction of `std::array` with binary operation - c++

I want to write a constexpr function, that reduces a given std::array with a binary operation. I.e. a function which implements
template <typename T, std::size_t N>
reduce(std::array<T, N>, binary_function);
To keep things simple I want to start with addition. E.g.
sum(std::array<int, 5>{{1,2,3,4,5}}); // returns 15.
What I got so far.
I use the usual indexing trick to index array elements. I.e. generate a int sequence, that can be used for indexing with parameter list-expansion.
template <int... Is>
struct seq {};
template <int I, int... Is>
struct gen_seq : gen_seq<I - 1, I - 1, Is...> {};
template <int... Is>
struct gen_seq<0, Is...> : seq<Is...> {}; // gen_seq<4> --> seq<0, 1, 2, 3>
The sum function is then defined through variadic template recursion.
// The edge-condition: array of one element.
template <typename T>
constexpr T sum(std::array<T, 1> arr, decltype(gen_seq<0>{})) {
return std::get<0>(arr);
}
// The recursion.
template <typename T, std::size_t N, int... Is>
constexpr auto sum(std::array<T, N> arr, seq<Is...>) -> decltype(T() + T()) {
return sum(std::array<T, N - 1>{ { std::get<Is>(arr)... } },
gen_seq<N - 2>()) +
std::get<N - 1>(arr);
}
// The interface - hides the indexing trick.
template <typename T, std::size_t N>
constexpr auto sum(std::array<T, N> arr)
-> decltype(sum(arr, gen_seq<N - 1>{})) {
return sum(arr, gen_seq<N - 1>{});
}
Here you can see it in action.
Questions
This implementation works. However, I do have a few questions at this stage.
Is there any way, I can add perfect-forward to this function? And does that even make sense? Or should I declare those arrays const-references?
The assumption so far is, that the return-type of the reduction is decltype(T()+T()). I.e. what you get when you add two elements. While this should be true for addition in most cases, it might no longer be true for a general reduction. Is there a way, of getting the type of a[0] + (a[1] + (a[2] + ... ) )? I tried something like this, but I don't know how I can produce a template parameter list of <T, T, T, ...>.

My answer is based on my own implementation of such staff.
I prefer the general reduce (or fold, or accumulate) function to operate directly on elements as its own function arguments rather than being within a container like std::array. This way, instead of constructing a new array in every recursion, elements are passed as arguments and I guess the whole operation is easier for the compiler to inline. Plus it's more flexible, e.g. could be used directly or on the elements of a std::tuple. The general code is here. I repeat here the main function:
template <typename F>
struct val_fold
{
// base case: one argument
template <typename A>
INLINE constexpr copy <A>
operator()(A&& a) const { return fwd<A>(a); }
// general recursion
template <typename A, typename... An>
INLINE constexpr copy <common <A, An...> >
operator()(A&& a, An&&... an) const
{
return F()(fwd<A>(a), operator()(fwd<An>(an)...));
}
};
I am sorry this is full of my own definitions, so here is some help: F is the function object defining the binary operation. copy is my generalization of std::decay that recurses within arrays and tuples. fwd is just a shortcut for std::forward. Similarly, common is just a shortcut for std::common_type but intended for a similar generalization (in general, each operation may yield an expression template for lazy evaluation and here we are forcing evaluation).
How would you define sum using the above? First define the function object,
struct sum_fun
{
template <typename A, typename B>
INLINE constexpr copy <common <A, B> >
operator()(A&& a, B&& b) const { return fwd<A>(a) + fwd<B>(b); }
};
then just
using val_sum = val_fold<sum_fun>;
How would you call this when starting with an std::array? Well, once you've got your Is..., all you need is
val_sum()(std::get<Is>(arr)...);
which you may wrap within your own interface. Note that in C++14, std::array::operator[] is constexpr, so this would just be
val_sum()(arr[Is]...);
Now, to your questions:
1) Forwarding: Yes, std::get is forwarding array elements into val_sum, which is recursively forwarding everything to itself. So all that remains is your own interface to forward the input array, e.g.
template <typename A, /* enable_if to only allow arrays here */>
constexpr auto sum(A&& a) -> /* return type here */
{
return sum(std::forward<A>(a), gen_seq_array<A>{});
}
and so on, where gen_seq_array would take the raw type of A (std::remove_ref, std::remove_cv etc.), deduce N, and call gen_seq<N>{}. Forwarding makes sense if array elements have move semantics. It should be everywhere, e.g. the call of val_sum above would be something like
val_sum()(std::get<Is>(std::forward<A>(a))...);
2) Return type: As you have seen, I am using std::common_type as the return type, which should do for sum and most common arithmetic operations. This is already variadic. If you'd like your own type function, it's easy to make a variadic out of a binary type function, using template recursion.
My own version of common is here. It's a bit more involved, but it is still a recursive template containing some decltype to do the actual work.
In any case, this is more general than what you need, because it's defined for an arbitrary number of any given types. You only have one type T in your array, so what you have should be enough.

Related

Making a cpp sorted tuple

Here's the code for std::make_tuple in the standard library.
template<typename... _Elements>
inline tuple<typename __decay_and_strip<_Elements>::__type...>
make_tuple(_Elements&&... __args)
{
typedef tuple<typename __decay_and_strip<_Elements>::__type...>
__result_type;
return __result_type(std::forward<_Elements>(__args)...);
}
What I would like to do, is to sort __args before creating the tuple, presumably with std::sort(..., Compare comp) where the user passes in an appropriate comparator that can be used to sort whatever type things end up in __args.
However, I'm relatively new to cpp, I don't understand half of the code in this function, and the std::sort needs an argument for the end of __args, and I'm not sure how to derive that.
Also please explain the typename __decay_and_strip<_Elements>::__type... and _Elements&&... bits...
EDIT Since for arbitrary type combinations the return type would then be unknown at compile time, the generic case seems to be not possible. Supposing all the same type, then, and we replace ..._Elements with T, I'm still unsure how to get the ".end()" of __args for use in std::sort
This can be done if the tuple type arguments are homogeneous. (We can't sort non-homogeneous types because that would require rearrangement of the types themselves, and that's not something you can do at compile time.1)
Assuming homogeneous types, the solution basically boils down to this:
Throw the arguments into an array.
Sort the array.
Make a tuple from the array contents.
This isn't too hard. First we need the indices trick to index our array (for step 3 -- you can use std::index_sequence instead if you have C++14):
template <std::size_t... Is>
struct indices {};
template <std::size_t N, std::size_t... Is>
struct build_indices
: build_indices<N-1, N-1, Is...> {};
template <std::size_t... Is>
struct build_indices<0, Is...> : indices<Is...> {};
Then we need a way to peel off the first type from the parameter pack to declare our array (for step 1). As a bonus, we'll have it check to make sure all the types are the same:
template <typename...>
struct pack_type;
template <typename Head>
struct pack_type<Head>
{
using type = Head;
};
// Will fail deduction on a non-homogeneous pack.
template <typename Head, typename... Tail>
struct pack_type<Head, Head, Tail...> : pack_type<Head, Tail...> {};
Finally, our sorter implementation with a helper to build the indices pack:
template <std::size_t... I, typename Comparer, typename... Ts>
std::tuple<Ts...> make_sorted_tuple_impl(indices<I...>, Comparer const &c, Ts && ...args)
{
typename pack_type<Ts...>::type values[sizeof...(Ts)] = { std::forward<Ts>(args)... };
std::sort(std::begin(values), std::end(values), c);
return std::make_tuple(std::forward<Ts>(values[I])...);
}
// Special case to handle empty tuples.
template <typename Comparer>
std::tuple<> make_sorted_tuple_impl(indices<>, Comparer const &)
{
return std::tuple<>();
}
template <typename Comparer, typename... Ts>
std::tuple<Ts...> make_sorted_tuple(Comparer const &c, Ts && ...args)
{
return make_sorted_tuple_impl(build_indices<sizeof...(Ts)>(), c, std::forward<Ts>(args)...);
}
See it run.
Also please explain the typename __decay_and_strip<_Elements>::__type... and _Elements&&... bits...
I'm not going to explain the first because identifiers containing __ are reserved by the C++ implementation, so this __decay_and_strip is an implementation detail specific to this particular C++ implementation.
_Elements&&... is a pack of rvalue references. This allows the arguments to be perfectly forwarded to the std::tuple constructor.
1 I lied. You can do it if the values and the compare function are constexpr, but the code to pull it off will be huge and not worth the time to write.

Pass Groups of Arguments in a Variadic Template

Variadic templates are really useful for doing recursive operations. In this case I'd like each recursive call to operate on two arguments so I don't have to repeatedly call the same function. To achieve this I can write:
f() {}
template<typename M,
typename N,
typename... Rest>
f(M arg1, N arg2, Rest... rest)
{
doStuff(arg1, arg2);
f(rest);
}
Then I would call this as such:
f(arg1a, arg1b,
arg2a, arg2b,
arg3a, arg3b);
However, if the call is not formatted so nicely, and all the arguments are on one line, or the column is split at the wrong point, it becomes quite unreadable. Especially so if the call might contain a dozen or so pairs. I attempted to remedy this by requiring a parameter pack of pairs to be passed in. I would like the function to have to be called like this:
f({arg1a, arg1b},
{arg2a, arg2b},
{arg3a, arg3b});
This seems to be mostly failing as the initializer list does not get deduced to a pair. I can call make_pair on each set of arguments, but that just solves a readability problem with another readability problem. Is there any way to get this sort of calling syntax to work? The arguments are not the same across pairs or within pairs.
Sorry, it is not possible: an implicit type conversion from braced list to a user-defined type. And the only conversion that is possible is for std::initializer_list, but it can be only done for the same types, or for convertible types.
With this said, I would offer this, as a possibility,
template <typename M, typename N>
void doStuff(M&&, N&&) {}
template <typename M, typename N>
struct MyRepeater;
template <typename M, typename N>
MyRepeater<M, N> f(M&& one, N&& two);
template <typename M, typename N>
struct MyRepeater {
template <typename I, typename J>
MyRepeater<I, J> operator()(I&& one, J&& two) const {
return f(std::forward<I>(one), std::forward<J>(two));
}
};
template <typename M, typename N>
MyRepeater<M, N> f(M&& one, N&& two) {
doStuff(one, two);
return MyRepeater<M, N>();
}
int main() {
f(Foo1(), Foo2())(Bar1(), Bar2())(Bar2(), Foo1());
}
Of course, the downside is that only for readability you have two write some extra code (in my opinion, it is not a bad motivation).
It may seem surprising but the best solution I've found for this is actually a "blast from the past" that doesn't use variadics at all.
struct F {
template <class M, class N>
F& operator()(M arg1, N arg2) {
doStuff(arg1, arg2);
return *this;
}
};
F{} (arg1a, arg1b)
(arg2a, arg2b);
And so on. Although, I'll also note that clang-format does not format this nicely. So I ended up doing something slightly different again.

Is it possible to write a static variadic template function, in a template class, that's able to take N parameters of type T?

I'm having trouble writing the variadic function that:
1) takes the appropriate number of parameters N-1 (N always > 2) of the appropriate type FooArray<T, N> and
2) allows me to play with them in the fiddly way I need to.
So, given the following template class:
template <typename T, unsigned int N>
class FooArray{
private:
T m_data[N];
};
and the following static variadic template function declaration:
template <typename T, unsigned int N>
class FooArray{
public:
template <typename ... ArgT>
static FooArray<T, N> Weirdness(FooArray<T, N> _arg1, ArgT ... _args);
private:
T m_data[N];
};
and associated implementation in that class:
template <typename T, unsigned int N>
template <typename ... ArgT>
FooArray<T, N>
FooArray<T, N>::Weirdness(T _arg1, ArgT ... _args)
{
static_assert(N > 2, "Not enough Ns for Weirdness.");
static_assert(N - 2 == sizeof... (_args), "Incorrect parameter number.");
FooArray<T, N> result;
// ...
// Put parameters into table of size N * (N-1) maybe?
// Do stuff with that to determine result.
// ...
return result;
}
I want to have the given code (or something like it) return me errors at the marked places and be valid in the marked places too.
int main()
{
FooArray<int, 2> test2parameter;
FooArray<int, 3> test3param1, test3param2;
FooArray<int, 4> test4p1, test4p2, test4p3;
//FooArray<int, 2>::Weirdness(test2parameter); // Should error(and does), not enough N's.
//FooArray<int, 3>::Weirdness(); // Should error(and does), requires 2 parameters.
//FooArray<int, 3>::Weirdness(test2parameter); // Should error(and does), requires 2 parameters of type FooArray<int, 3>.
FooArray<int, 3>::Weirdness(test3param1, test2parameter); // Should error(currently doesn't), all parameters should be FooArray<int, 3>.
FooArray<int, 3>::Weirdness(test3param1, test3param2); // Valid.
//FooArray<int, 4>::Weirdness(); // Should error (and does), requires 3 parameters.
//FooArray<int, 4>::Weirdness(test4p1, test4p2); // Should error (and currently does), requires 3 parameters.
FooArray<int, 4>::Weirdness(test4p1, test4p2, test3param1); // Should error (currently doesn't), all parameters should be FooArray<int, 4>.
FooArray<int, 4>::Weirdness(test4p1, test4p2, test4p3); // Valid.
//FooArray<int, 12>::Weirdness(test12p1, test12p2, test12p3, test12p4, test12p5, test12p6, test12p7, test12p8, test12p9, test12p10, test12p11); // Will be a valid case.
return 0;
}
Re: the initial question, I'm 90% there. Once I have the compiler throwing errors at me for passing the function an incorrect parameter type, the question is answered.
Thank you for your time. (Seriously, stop reading now if you're short on time or are trying to solve your own problem)
Re: Why would you want to do this? I am trying to write an N-Dimensional vector (geometry) cross product function, because I think mathematicians are silly for thinking 3D vectors are the only ones that can have a cross product. I'm certain I can do it if I can write this function. Plus, I'm an indie game developer, this function has interesting uses for me in a handful of games I've got kicking around in my brain.
Bonus material: I have another problem that using variadic templates creates for me. Once I have all these FooArrays inside my Weirdness() function, I have to do a calculation that requires (for every member in the return variable) access to two different members of every passed parameter. So perhaps this is a poor design choice? I'm considering a recursive private static variadic template function to perhaps populate a static array of Ts of size N*(N-1). The existing function doesn't allow this fine fiddle I require. This design bit is all very open ended though and perhaps warrants another question on a more open-ended-question friendly platform. :)
Add one more static assertion:
static_assert(are_same<FooArray<T, N>, ArgT...>::value, "Types do not all match");
And implement are_same<>:
template <typename A1, typename A2, typename ... Rest>
struct are_same
{
enum { value = std::is_same<A1, A2>::value && are_same<A2, Rest...>::value };
};
template <typename A1, typename A2>
struct are_same<A1, A2>
{
enum { value = std::is_same<A1, A2>::value };
};
I would prefer a simpler function declaration and using John Zwinck's method to ensure the parameter types are all correct.
template <typename T, unsigned int N>
template <typename ... ArgT>
FooArray<T, N>
FooArray<T, N>::Weirdness(ArgT ... _args)
{
static_assert(are_same<FooArray<T, N>, ArgT...>::value, "Types do not all match");
static_assert(N > 2, "Not enough Ns for Weirdness.");
static_assert(N - 1 == sizeof... (_args), "Incorrect parameter number.");

Tuple Iteration [duplicate]

So I'm trying to figure out how this works: C++11: I can go from multiple args to tuple, but can I go from tuple to multiple args?
The piece of black magic I do not understand is this code fragment:
f(std::get<N>(std::forward<Tuple>(t))...)
it's the expression inside f that I don't understand.
I understand that the expression somehow unpacks/expands what's inside t into a list of arguments. But could someone care to explain how this is done? When I look at the definition of std::get (http://en.cppreference.com/w/cpp/utility/tuple/get), I don't see how N fits in...? As far as I can tell, N is a sequence of integers.
Based on what I can observe, I'm assuming that expressions in the form E<X>... where X is the sequence of types X1. X2, ... Xn, the expression will be expanded as E<X1>, E<X2> ... E<Xn>. Is this how it works?
Edit: In this case N is not a sequence of types, but integers. But I'm guessing this language construct applies to both types and values.
I think that #Xeo's comment summed it up well. From 14.5.3 of the C++11 standard:
A pack expansion consists of a pattern and an ellipsis, the
instantiation of which produces zero or more instantiations of the
pattern in a list.
In your case, by the time you finish with the recursive template instantiation and end up in the partial specialization, you have
f(std::get<N>(std::forward<Tuple>(t))...);
...where N is parameter pack of four ints (0, 1, 2, and 3). From the standardese above, the pattern here is
std::get<N>(std::forward<Tuple>(t))
The application of the ... ellipsis to the above pattern causes it to be expanded into four instantiations in list form, i.e.
f(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t));
The fundamental ingredient to expanding the std::tuple<T...> is actually omitted from the code: you need to obtain a a second parameter back: in addition to the list of types of the std::tuple<...> you need a parameter pack with indices 0, 1, ..., n. Once you have these two parameters packs, you can expand them in tandem:
template <typename F, typename... T, int... N>
void call_impl(F&& fun, std::tuple<T...>&& t) {
fun(std::get<N>(t)...);
}
The real magic lies in conjuring up the second parameter pack when you just have a std::tuple<T...>. It takes a bit of template programming. Here is an approach to create the list of indices:
template <int... Indices> struct indices;
template <> struct indices<-1> { typedef indices<> type; };
template <int... Indices>
struct indices<0, Indices...>
{
typedef indices<0, Indices...> type;
};
template <int Index, int... Indices>
struct indices<Index, Indices...>
{
typedef typename indices<Index - 1, Index, Indices...>::type type;
};
template <typename T>
typename indices<std::tuple_size<T>::value - 1>::type const*
make_indices()
{
return 0;
}
So, if you have a function template, let's call it call() which takes a function object and a std::tuple<T...> with the arguments to the function. An easy approach is to rewrite the call_impl() mentioned above to deal with deducing the indices:
template <typename F, typename Tuple, int... N>
void call_impl(F&& fun, Tuple&& t, indices<Indices...> const*)
{
fun(std::get<N>(t)...);
}
template <typename F, typename Tuple>
void call(F&& fun, Tuple&& t)
{
call_imle(std::forward<F>(fun), std::forward<Tuple>(t), make_indices<Tuple>());
}
What this code doesn't really extend is the correct use of std::forward<...>() with the various std::tuple<...> elements when calling the function. Just using std::forward<Tuple>(t) does not work because it possibly moves the entire std::tuple<...> rather than moving the elements. I think something like a suitable element-wise move of a std::tuple<...> can be done but I haven't done it, yet.

Unpacking arguments from tuples

So I'm trying to figure out how this works: C++11: I can go from multiple args to tuple, but can I go from tuple to multiple args?
The piece of black magic I do not understand is this code fragment:
f(std::get<N>(std::forward<Tuple>(t))...)
it's the expression inside f that I don't understand.
I understand that the expression somehow unpacks/expands what's inside t into a list of arguments. But could someone care to explain how this is done? When I look at the definition of std::get (http://en.cppreference.com/w/cpp/utility/tuple/get), I don't see how N fits in...? As far as I can tell, N is a sequence of integers.
Based on what I can observe, I'm assuming that expressions in the form E<X>... where X is the sequence of types X1. X2, ... Xn, the expression will be expanded as E<X1>, E<X2> ... E<Xn>. Is this how it works?
Edit: In this case N is not a sequence of types, but integers. But I'm guessing this language construct applies to both types and values.
I think that #Xeo's comment summed it up well. From 14.5.3 of the C++11 standard:
A pack expansion consists of a pattern and an ellipsis, the
instantiation of which produces zero or more instantiations of the
pattern in a list.
In your case, by the time you finish with the recursive template instantiation and end up in the partial specialization, you have
f(std::get<N>(std::forward<Tuple>(t))...);
...where N is parameter pack of four ints (0, 1, 2, and 3). From the standardese above, the pattern here is
std::get<N>(std::forward<Tuple>(t))
The application of the ... ellipsis to the above pattern causes it to be expanded into four instantiations in list form, i.e.
f(std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t));
The fundamental ingredient to expanding the std::tuple<T...> is actually omitted from the code: you need to obtain a a second parameter back: in addition to the list of types of the std::tuple<...> you need a parameter pack with indices 0, 1, ..., n. Once you have these two parameters packs, you can expand them in tandem:
template <typename F, typename... T, int... N>
void call_impl(F&& fun, std::tuple<T...>&& t) {
fun(std::get<N>(t)...);
}
The real magic lies in conjuring up the second parameter pack when you just have a std::tuple<T...>. It takes a bit of template programming. Here is an approach to create the list of indices:
template <int... Indices> struct indices;
template <> struct indices<-1> { typedef indices<> type; };
template <int... Indices>
struct indices<0, Indices...>
{
typedef indices<0, Indices...> type;
};
template <int Index, int... Indices>
struct indices<Index, Indices...>
{
typedef typename indices<Index - 1, Index, Indices...>::type type;
};
template <typename T>
typename indices<std::tuple_size<T>::value - 1>::type const*
make_indices()
{
return 0;
}
So, if you have a function template, let's call it call() which takes a function object and a std::tuple<T...> with the arguments to the function. An easy approach is to rewrite the call_impl() mentioned above to deal with deducing the indices:
template <typename F, typename Tuple, int... N>
void call_impl(F&& fun, Tuple&& t, indices<Indices...> const*)
{
fun(std::get<N>(t)...);
}
template <typename F, typename Tuple>
void call(F&& fun, Tuple&& t)
{
call_imle(std::forward<F>(fun), std::forward<Tuple>(t), make_indices<Tuple>());
}
What this code doesn't really extend is the correct use of std::forward<...>() with the various std::tuple<...> elements when calling the function. Just using std::forward<Tuple>(t) does not work because it possibly moves the entire std::tuple<...> rather than moving the elements. I think something like a suitable element-wise move of a std::tuple<...> can be done but I haven't done it, yet.