Концептуально очень похоже, но с двумя измерениями становится кошмаром.
Вместо dimVec
я предлагаю dimMat
с двумя измерениями (N
и M
) и отказ SFINAE.для неприемлемых случаев (vecs или маты разных размеров, vect с размерами, несовместимыми с матом);не уверен, что все дела управляются, но должно быть что-то вроде
template <std::size_t, std::size_t, typename ...>
struct dimMat;
// ground case for no Vecs and no Mats: unimplemented for SFINAE failure
template <>
struct dimMat<0U, 0U>;
// ground case with one or more Vecs and/or Mats: sizes fixed
template <std::size_t N, std::size_t M>
struct dimMat<N, M>
{
static constexpr std::size_t valN { N };
static constexpr std::size_t valM { M };
};
// first Vec: M detected
template <std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, 0U, Vec<T, M>, Ts...> : public dimMat<0U, M, Ts...>
{ };
// first Mat: N and M detected
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, 0U, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
{ };
// first Mat after a correct Vect: N detected
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, M, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
{ };
// another Vec of correct size: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, Vec<T, M>, Ts...> : public dimMat<N, M, Ts...>
{ };
// another Mat of correct sizes: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
{ };
// another Vec of different size: unimplemented for SFINAE failure
template <std::size_t N, std::size_t M1, std::size_t M2, typename T,
typename ... Ts>
struct dimMat<N, M1, Vec<T, M2>, Ts...>;
// another Mat of different sizes: unimplemented for SFINAE failure
template <std::size_t N1, std::size_t N2, std::size_t M1, std::size_t M2,
typename T, typename ... Ts>
struct dimMat<N1, M1, Mat<T, N2, M2>, Ts...>;
// a not-Vec, not-Mat type: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, T, Ts...> : public dimMat<N, M, Ts...>
{ };
Переменные вспомогательного шаблона просты
template <typename ... Args>
static constexpr auto dimMatN { dimMat<0U, 0U, Args...>::valN };
template <typename ... Args>
static constexpr auto dimMatM { dimMat<0U, 0U, Args...>::valM };
Для apply()
, переименован applyM()
, я предполагаю что-то как
template <typename F, typename ... Args, std::size_t N = dimMatN<Args...>,
std::size_t M = dimMatM<Args...>>
auto applyM (F && f, Args ... as)
{ return applyMH1(std::make_index_sequence<N>{},
std::make_index_sequence<M>{},
f, as...); }
Для applyMH1()
двух случаев: если N
равен нулю, поэтому первый аргумент является пустым индексным списком, мы имеем случай vec
// Vec case: the first index list is empty: call applyH2()
template <std::size_t ... Js, typename F, typename ... Args>
auto applyMH1 (std::index_sequence<> const &,
std::index_sequence<Js...> const &, F && f, Args ... as)
-> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Js)>
{ return { applyH2<Js>(f, as...)... }; }
в противном случае матcase
template <std::size_t ... Is, std::size_t ... Js, typename F,
typename ... Args>
auto applyMH1 (std::index_sequence<Is...> const &,
std::index_sequence<Js...> const & js, F && f, Args ... as)
-> Mat<decltype(applyMH3<0U, 0U>(f, as...)), sizeof...(Is), sizeof...(Js)>
{ return {{{ applyMH2<Is>(js, f, as...) ... }}}; }
Now applyMH2()
template <std::size_t I, std::size_t ... Js, typename F, typename ... Args>
auto applyMH2 (std::index_sequence<Js...> const &, F && f, Args ... as)
-> std::array<decltype(applyMH3<0U, 0U>(f, as...)), sizeof...(Js)>
{ return {{ applyMH3<I, Js>(f, as...)... }}; }
и applyMH3()
template <std::size_t I, std::size_t J, typename F, typename ... Args>
auto applyMH3 (F && f, Args ... as)
{ return f(extrM<I, J>(as)...); }
с учетом extrM()
на основе extrV()
template <std::size_t I, std::size_t J, typename T,
std::size_t N, std::size_t M>
constexpr auto extrM (Mat<T, N, M> const & v)
{ return v[I][J]; }
template <std::size_t I, std::size_t J, typename T>
constexpr auto extrM (T const & v)
{ return extrV<J>(v); }
Ниже приведен полный пример компиляции
#include <array>
#include <iostream>
#include <type_traits>
template <typename T, std::size_t N>
class Vec;
template <typename T, std::size_t N, std::size_t M>
struct Mat;
template <std::size_t, std::size_t, typename ...>
struct dimMat;
// ground case for no Vecs and no Mats: unimplemented for SFINAE failure
template <>
struct dimMat<0U, 0U>;
// ground case with one or more Vecs and/or Mats: sizes fixed
template <std::size_t N, std::size_t M>
struct dimMat<N, M>
{
static constexpr std::size_t valN { N };
static constexpr std::size_t valM { M };
};
// first Vec: M detected
template <std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, 0U, Vec<T, M>, Ts...> : public dimMat<0U, M, Ts...>
{ };
// first Mat: N and M detected
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, 0U, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
{ };
// first Mat after a correct Vect: N detected
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<0U, M, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
{ };
// another Vec of correct size: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, Vec<T, M>, Ts...> : public dimMat<N, M, Ts...>
{ };
// another Mat of correct sizes: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, Mat<T, N, M>, Ts...> : public dimMat<N, M, Ts...>
{ };
// another Vec of different size: unimplemented for SFINAE failure
template <std::size_t N, std::size_t M1, std::size_t M2, typename T,
typename ... Ts>
struct dimMat<N, M1, Vec<T, M2>, Ts...>;
// another Mat of different sizes: unimplemented for SFINAE failure
template <std::size_t N1, std::size_t N2, std::size_t M1, std::size_t M2,
typename T, typename ... Ts>
struct dimMat<N1, M1, Mat<T, N2, M2>, Ts...>;
// a not-Vec, not-Mat type: continue
template <std::size_t N, std::size_t M, typename T, typename ... Ts>
struct dimMat<N, M, T, Ts...> : public dimMat<N, M, Ts...>
{ };
template <typename ... Args>
static constexpr auto dimMatN { dimMat<0U, 0U, Args...>::valN };
template <typename ... Args>
static constexpr auto dimMatM { dimMat<0U, 0U, Args...>::valM };
template <std::size_t, typename ...>
struct dimVec;
// ground case for no Vecs: unimplemented !
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 !
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 <std::size_t I, typename T, std::size_t N, std::size_t M>
constexpr auto extrV (Mat<T, N, M> const & v)
{ return 0; }
template <std::size_t I, std::size_t J, typename T,
std::size_t N, std::size_t M>
constexpr auto extrM (Mat<T, N, M> const & v)
{ return v[I][J]; }
template <std::size_t I, std::size_t J, typename T>
constexpr auto extrM (T const & v)
{ return extrV<J>(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 <typename T, std::size_t N, std::size_t M>
struct Mat
{
std::array<std::array<T, M>, N> m;
auto & operator[] (int i)
{ return m[i]; }
auto const & operator[] (int i) const
{ return m[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...); }
template <std::size_t I, std::size_t J, typename F, typename ... Args>
auto applyMH3 (F && f, Args ... as)
{ return f(extrM<I, J>(as)...); }
template <std::size_t I, std::size_t ... Js, typename F, typename ... Args>
auto applyMH2 (std::index_sequence<Js...> const &, F && f, Args ... as)
-> std::array<decltype(applyMH3<0U, 0U>(f, as...)), sizeof...(Js)>
{ return {{ applyMH3<I, Js>(f, as...)... }}; }
// Vec case: the first index list is empty: call applyH2()
template <std::size_t ... Js, typename F, typename ... Args>
auto applyMH1 (std::index_sequence<> const &,
std::index_sequence<Js...> const &, F && f, Args ... as)
-> Vec<decltype(applyH2<0U>(f, as...)), sizeof...(Js)>
{ return { applyH2<Js>(f, as...)... }; }
template <std::size_t ... Is, std::size_t ... Js, typename F,
typename ... Args>
auto applyMH1 (std::index_sequence<Is...> const &,
std::index_sequence<Js...> const & js, F && f, Args ... as)
-> Mat<decltype(applyMH3<0U, 0U>(f, as...)), sizeof...(Is), sizeof...(Js)>
{ return {{{ applyMH2<Is>(js, f, as...) ... }}}; }
template <typename F, typename ... Args, std::size_t N = dimMatN<Args...>,
std::size_t M = dimMatM<Args...>>
auto applyM (F && f, Args ... as)
{ return applyMH1(std::make_index_sequence<N>{},
std::make_index_sequence<M>{},
f, as...); }
long foo (int a, int b)
{ return a + b + 42; }
int main ()
{
Vec<int, 3U> v3;
Vec<int, 2U> v2;
Mat<int, 2U, 3U> m23;
Mat<int, 2U, 4U> m24;
auto r1 { applyM(foo, v2, v2) };
auto r2 { applyM(foo, v3, v3) };
auto r3 { applyM(foo, v3, 0) };
auto r4 { applyM(foo, v3, m23) };
auto r5 { applyM(foo, m24, 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>>{}, "!" );
static_assert( std::is_same<decltype(r4), Mat<long, 2U, 3U>>{}, "!" );
static_assert( std::is_same<decltype(r5), Mat<long, 2U, 4U>>{}, "!" );
//applyM(foo, v2, v3); // compilation error
//applyM(foo, 1, 2); // compilation error
//applyM(foo, v2, m23); // compilation error
//applyM(foo, m24, m23); // compilation error
}