I have been hunting for a solution for this for a while, and have not been able to find a satisfying solution to this problem:
Suppose I have a variadic type:
template<typename... layers_t>
class Layer_Aggregate
{
};
And I have some alias of the above like this:
using stack_t = Layer_Aggregate<
Matrix<3,3>,
Matrix<3,3>
>;
There's a couple of ways to define a variadic function that folds over a bunch of objects:
This got way easier in C++17 if that's available to you. Just define an operator for your matrix:
Matrix<3, 3> operator *(const Matrix<3, 3>& lhs, const Matrix<3, 3>& rhs) { /* ... */ }
Then it's as easy as using a fold expression
template <typename... Ts>
auto aggregate(Ts&&... ts) {
return (ts * ...);
}
Which will do a right fold over all the matrices until they're aggregated into one.
Usage:
Matrix<3, 3> a = /*...*/;
Matrix<3, 3> b = /*...*/;
Matrix<3, 3> c = /*...*/;
auto abc = aggregate(a, b, c);
Demo: https://godbolt.org/z/uf54Hk
What if your reduce function isn't available as an operator? For instance:
Matrix<3, 3> multiply(const Matrix<3, 3>& lhs, const Matrix<3, 3>& rhs)
You can still wrap it in a helper struct and define an operator for that:
template <auto Op, typename T>
struct RefWrapper { const T& ref; };
template <auto Op, typename T, typename U>
auto operator *(RefWrapper<Op, T> t, RefWrapper<Op, U> u) {
return RefWrapper<Op, decltype(Op(t.ref, u.ref))>{ Op(t.ref, u.ref) };
}
template <auto Op, typename... Ts>
auto aggregate(Ts&&... ts) {
return (RefWrapper<Op, Ts>{ ts } * ...).ref;
}
Demo: https://godbolt.org/z/APhT-v
In C++11/14, it's a bit more difficult since you have to manually recurse through each argument. You also can't auto-deduce function pointer signatures, so you have to add a separate template argument to deduce the field.
Start with a placeholder struct.
template <typename Sig, Sig Op, typename... Ts>
struct LayerAggregate;
Then partially specialize for the recursive case. This will pluck off the first argument, and multiply it with the aggregate of the remaining arguments. We can also use the same partial specialization to deduce the return type of the operator:
template <typename T, T (*Op)(const T&, const T&), typename TFirst, typename... TRest>
struct LayerAggregate<T(*)(const T&, const T&), Op, TFirst, TRest...> {
T operator()(TFirst&& first, TRest&&... rest) {
T restAggregate = LayerAggregate<T(*)(const T&, const T&), Op, TRest...>{}(std::forward<TRest>(rest)...);
return Op(first, restAggregate);
}
};
Finally, add a terminal case, to stop recursing when it gets to the last item:
template <typename T, T (*Op)(const T&, const T&), typename TLast>
struct LayerAggregate<T(*)(const T&, const T&), Op, TLast> {
T operator()(TLast&& last) {
return last;
}
};
If you're using C++14, the wrapper is easy if you ask the compiler to deduce the return type:
template <typename Sig, Sig Op, typename... Ts>
auto Aggregate(Ts&&... ts) {
return LayerAggregate<Sig, Op, Ts...>{}(std::forward<Ts>(ts)...);
}
For C++11, you still have to deduce it yourself though:
template <typename Sig, Sig Op, typename... Ts>
struct OpResult;
template <typename Sig, Sig Op, typename T, typename... Ts>
struct OpResult<Sig, Op, T, Ts...> {
using Type = decltype(Op(std::declval<T>(), std::declval<T>()));
};
template <typename Sig, Sig Op, typename... Ts>
using OpResultT = typename OpResult<Sig, Op, Ts...>::Type;
template <typename Sig, Sig Op, typename... Ts>
OpResultT<Sig, Op, Ts...> Aggregate(Ts&&... ts) {
return LayerAggregate<Sig, Op, Ts...>{}(std::forward<Ts>(ts)...);
}
Finally, to call, you just have to pass the function signature as well as the function:
Matrix<3, 3> a = /*...*/;
Matrix<3, 3> b = /*...*/;
Matrix<3, 3> c = /*...*/;
auto abc = Aggregate<decltype(&multiply), &multiply>(a, b, c);