This question is a follow on from my previous question Multi patterned varadic templates in C++ to which I received the solution:
#include <array>
#include <iostream>
#include <type_traits>
template <typename T, std::size_t N>
class Vec;
template <std::size_t, typename ...>
struct dimVec;
// ground case for no Vecs: unimplemented for SFINAE failure !
template <>
struct dimVec<0U>;
// ground case with one or more Vecs: size fixed
template <std::size_t N>
struct dimVec<N> : public std::integral_constant<std::size_t, N>
{ };
// first Vec: size detected
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<0U, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of same size: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, Vec<T, N>, Ts...> : public dimVec<N, Ts...>
{ };
// another Vec of different size: unimplemented for SFINAE failure !
template <std::size_t N1, std::size_t N2, typename T, typename ... Ts>
struct dimVec<N1, Vec<T, N2>, Ts...>;
// a not-Vec type: continue
template <std::size_t N, typename T, typename ... Ts>
struct dimVec<N, T, Ts...> : public dimVec<N, Ts...>
{ };
template <typename ... Args>
static constexpr auto dimVecV { dimVec<0U, Args...>::value };
template <std::size_t I, typename T, std::size_t N>
constexpr auto extrV (Vec<T, N> const & v)
{ return v[I]; }
template <std::size_t I, typename T>
constexpr auto extrV (T const & v)
{ return v; }
template <typename T, std::size_t N>
class Vec
{
private:
std::array<T, N> d;
public:
template <typename ... Ts>
Vec (Ts ... ts) : d{{ ts... }}
{ }
T & operator[] (int i)
{ return d[i]; }
T const & operator[] (int i) const
{ return d[i]; }
};
template <std::size_t I, typename F, typename ... Args>
auto applyH2 (F && f, Args ... as)
{ return f(extrV<I>(as)...); }
template <std::size_t ... Is, typename F, typename ... Args>
auto applyH1 (std::index_sequence<Is...> const &, F && f, Args ... as)
-> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Is)>
{ return { applyH2<Is>(f, as...)... }; }
template <typename F, typename ... Args, std::size_t N = dimVecV<Args...>>
auto apply (F && f, Args ... as)
{ return applyH1(std::make_index_sequence<N>{}, f, as...); }
long foo (int a, int b)
{ return a + b + 42; }
int main ()
{
Vec<int, 3U> v3;
Vec<int, 2U> v2;
auto r1 { apply(foo, v2, v2) };
auto r2 { apply(foo, v3, v3) };
auto r3 { apply(foo, v3, 0) };
static_assert( std::is_same<decltype(r1), Vec<long, 2U>>{}, "!" );
static_assert( std::is_same<decltype(r2), Vec<long, 3U>>{}, "!" );
static_assert( std::is_same<decltype(r3), Vec<long, 3U>>{}, "!" );
// apply(foo, v2, v3); // compilation error
// apply(foo, 1, 2); // compilation error
}
now lets say I also have a template class template <typename T, size_t N, size_t M> class mat;
which represents a NxM
matrix with the previous Vec
's being equivalent to columns (M
) for broadcasting, now I want to extend apply so it can also work with the Mat
class with out breaking handling of just vec
's.
Rules:
- if the argument list contains a
NxM
matrix then all matricesNxM
and all vectors must beM
, and the function will return aNxM
matrix - otherwise rules as previously for vectors, if the argument list
contains a
N
vector then all vectors must beN
, and the function will return aN
vector
As far as I can tell since the function f()
isn't implemented for matrices then the applyH2()
should fail (if not then it needs to), in its current form, thus apply would fail, hence it should be possible to implement another apply()
which accepts multiple sizes M
and N
in the same way, which will be used if the argument list contains a matrix, and then invoke extrV<I,J>(as)
which for vectors should be mapped as extrV<I,J>(as) -> extrV<J>(as)
this part seams pretty straight forward as dose implementing dimMatM
and dimMatN
in the same way as dimVec
and such that dimMatM
maps to dimVec
for vectors.
However I'm not sure how to go about getting the two versions on apply()
to work together with out error.
Optional: This part isn't really important by lets say that later I have a class tensor<T, N...>
with extends the concept on vec and mat to N dimensions, would it be possible to extend apply to work such that broadcasting occurs over the leading dimensions i.e.
double f(float x, float y);
int main() {
tensor<float, 2, 4, 6> f246;
tensor<int, 4, 6> i46;
tensor<float, 2, 4> f24;
auto r1 = apply(f, f246, i46); // returns tensor<double, 2, 4, 6>
// apply(f, f24, f246); // Compile ERROR: dimensions don't match
}
EDIT:
I just thought up a possible solution or at least part of it see below
template <size_t ...N>
struct tensor_helper {
template <size_t I, typename T, size_t ...M> static tensorGet(const tensor<T, M...>& V) { return V.v[I % product<M...>::value]; }
template <size_t I, typename T> static tensorGet(const T& V) { return V; }
template <std::size_t I, typename F, typename ...Args>
static auto applyH2 (F && f, Args ...as) -> decltype(f(std::declval<typename base_type<Args>::type>()...))
{ return f(tensorGet<I>(as)...); }
template <size_t ... Is, typename F, typename ... Args>
static auto applyH1(std::index_sequence<Is...> const &..., F && f, Args ... as) -> tensor<decltype(f(std::declval<typename base_type<Args>::type>()...)), N...>
{ return make_tensor<decltype(f(std::declval<typename base_type<Args>::type>()...)), N...>({applyH2<Is>(f, as...)... }); }
}
template <typename F, typename ...Args, size_t ...N = tensorSize<Args...> /* How do I get a list here? */>
auto apply (F && f, Args ... as)
{ return tensor_helper<N...>::applyH1(std::make_index_sequence<product<N...>>{}..., f, as...); }
the only problem is how to get tensorSize()
to provide a list of dims, all though I assume this could be manually expanded if just implemented for vectors and matrices, rather than using the parameter packs for dims.
EDIT 2:
I belive I can use constexpr array to variadic template, to solve the N
list problem, from my above code. But I'm too tried too actualy try implementing that concept tonight will have a try tomorrow.