在C ++中扩展多模式可变参数模板 [英] Extending Multi patterned variadic templates in C++

查看:88
本文介绍了在C ++中扩展多模式可变参数模板的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

此问题是我上一个问题的后续内容 C ++中的多模式Varadic模板我收到的解决方案:

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

 }

现在让我说我也有一个模板class template< typename T,size_t N,size_t M>类垫; 表示 NxM 矩阵,而以前的 Vec 等于列( M )进行广播,现在我想扩展应用范围,以便它也可以与 Mat 类一起使用,而无需仅仅破坏 vec 的处理。

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.

规则:


  • 如果参数列表包含 NxM 矩阵,则所有矩阵 NxM
    所有向量必须为 M ,该函数将返回 NxM 矩阵

  • 否则,矢量的规则如前所述,如果参数列表
    包含 N 个矢量,则所有矢量必须为 N ,该函数将
    返回一个 N 向量

  • if the argument list contains a NxM matrix then all matrices NxM and all vectors must be M, and the function will return a NxM matrix
  • otherwise rules as previously for vectors, if the argument list contains a N vector then all vectors must be N, and the function will return a N vector

据我所知,由于函数 f()并未针对矩阵实现,因此 applyH2()应该失败(如果不是,那么它需要以当前形式),因此apply将失败,因此应应该有可能实现另一个 apply(),它接受多个大小 M N 以相同的方式使用,如果参数列表包含矩阵,则将使用它,然后调用 extrV< I,J>(as)应该映射为 extrV< I,J>(as)-> extrV< J>(as)由于实现 dimMatM dimMatN dimVec 相同,因此 dimMatM 映射到 dimVec 作为向量。

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.

但是我不确定如何在 apply()一起工作,而不会出错。

However I'm not sure how to go about getting the two versions on apply() to work together with out error.

可选:这部分并不是很重要,可以说以后我有一个类张量< ; T,N ...> 可以将vec和mat的概念扩展到N个维度,是否有可能将其应用于工作,从而使广播发生在主要维度上,即

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
}

编辑:

我只是想出了一个可能的解决方案,或者至少部分解决方案如下

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...); }

唯一的问题是如何获取 tensorSize()提供了一个暗角列表,尽管我认为如果只针对矢量和矩阵实现,可以手动扩展它,而不是使用用于暗角的参数包。

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.

编辑2:
我相信我可以使用 constexpr数组来可变模板,从上面的代码中解决 N 列表问题。但是我太想尝试了,今晚尝试实现该概念将在明天尝试。

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.

推荐答案

从概念上讲,它非常相似,但是,

Conceptually is very similar but, with two dimensions, become a nightmare.

我建议使用 dimMat代替 dimVec 具有两个维度( N M )和SFINAE失败的情况(不同大小) vecs或脚垫,尺寸与脚垫不相容的vect);不确定是否所有情况都得到管理,但应该为

Instead of dimVec, I propose a dimMat with two dimensions (N and M) and SFINAE failure for unacceptable cases (different sizes vecs or mats, vect with size incompatible with mat); not sure that all case are managed but should be something as

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...>
 { };

帮助程序模板变量很简单

The helper template variables are simple

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(),我想是这样的

For apply(), renamed applyM(), I suppose something 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...); }

对于 applyMH1()两种情况:如果 N 为零,则第一个参数为空索引列表,则为vec情况。

For applyMH1() two cases: if N is zero, so the first argument is an empty index list, we have the vec case

// 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...) ... }}}; }

现在 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
 }

这篇关于在C ++中扩展多模式可变参数模板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆