简体   繁体   English

拼合/重构C ++模板源以用于某些输入

[英]Flatten / Refactor C++ template source for certain inputs

I've got some template code that I'm trying to refactor. 我有一些要重构的模板代码。 Specifically, it's a geometric type, templated by parametric dimension (so it can represent a curve, surface, volume, hypervolume, and so forth), as well as point type. 具体来说,它是一种几何类型,由参数尺寸(因此可以表示曲线,曲面,体积,超体积等)进行模板化,以及点类型。

The problem is that it is getting really unwieldy to edit in such a generic way, and most of the time we only ever use parametric dimensions 1, 2, and 3. The partial specializations are the only things that are changing these days, the shared code is quite stable and complete. 问题在于,以这种通用方式进行编辑变得非常笨拙,并且在大多数情况下,我们仅使用参数维1、2和3。部分专业化是当今唯一改变的部分,代码是相当稳定和完整的。

Aside from the difficulty in editing the code, there are also some performance problems that stem from having to store the internal data in a way that generalizes to a grid of arbitrary dimension.. it is possible to mitigate the perf problems in generic ways, but that would just continue to add a lot of unnecessary complexity. 除了难以编辑代码外,还存在一些性能问题,这些问题源于必须以泛化为任意维的网格的方式存储内部数据。可以通过通用方式缓解性能问题,但是那只会继续增加很多不必要的复杂性。 Basically the problem is that the templates are too generalized. 基本上,问题在于模板过于笼统。

So, I'm going to replace the generic template code with 3 separate templates, one for each dimension. 因此,我将用3个单独的模板替换通用模板代码,每个模板一个。 I also want to keep the templating of the point type, so I don't want to just use plain classes. 我也想保留点类型的模板,所以我不想只使用简单的类。

If I had done templating the C way with macros and #including files multiple times, I could run the input through the preprocessor and get the 3 different versions that I want. 如果我多次使用宏和#include文件对C方式进行模板化,则可以通过预处理器运行输入,并获得所需的3个不同版本。

While I could do it by hand, I'd prefer an automated solution, at least as a starting point. 虽然我可以手工完成,但至少从开始的角度来看,我更喜欢自动化的解决方案。

Are there any similar methods or refactoring solutions that exist for C++, to get the source for a template with a specific input? C ++是否有类似的方法或重构解决方案,以获取具有特定输入的模板的来源?


To be a bit more concrete, I have code like this: 更具体一点,我有这样的代码:

template< int Dimension, class PointType > class Nurbs { ... }
template< class PointType > class NurbsCurve : public Nurbs< 1, PointType > { ... }
template< class PointType > class NurbsSurface : public Nurbs< 2, PointType > { ... }
template< class PointType > class NurbsVolume : public Nurbs< 3, PointType > { ... }

But I want to end up with code like this: 但我想以这样的代码结束:

template< class PointType > class NurbsCurve { ... }
template< class PointType > class NurbsSurface { ... }
template< class PointType > class NurbsVolume { ... }

This is not really answering your question, but it's an alternative way to keep the templated code. 这并不能真正回答您的问题,但这是保留模板代码的另一种方法。

If I understand correctly, your code has so many specialisations that it becomes unwieldy. 如果我理解正确,那么您的代码有很多专业化知识,以至于变得笨拙。 One way to deal with this is to use some helper template class dealing with all the details and call its static members from the other templates. 一种解决方法是使用一些处理所有详细信息的帮助程序模板类,并从其他模板调用其静态成员。 The helper class would have a base implementing generic (dimension-independent code) and then there will be specialisations which only override whatever needs to be overridden for the specific dimension. helper类将具有一个实现通用(与维度无关的代码)的基础,然后将有专门化,这些专门化仅覆盖需要为特定维度覆盖的所有内容。

namespace details {
  template<int Dimension> struct helper_base  // generic code
  {
    static_assert(Dimension>1,"missing specialisation Dimension=0,1");
    static const int Last = Dimension-1;

    template<typename T>
    static T dot_product(const T*a, const T*b) noexcept
    { return helper<Last>::dot_product(a,b) + a[Last]*b[Last]; }
  };

  template<> struct helper_base<1>
  {
    template<typename T>
    static T dot_product(const T*a, const T*b) noexcept
    { return a[0]*b[0]; }
  };

  template<int Dimension> struct helper // special code for certain dimensions
    : helper_base<Dimension> {};
  template<> struct helper<3> : helper_base<3>
  {
    // any code that is particular to 3D.
    template<typename T>
    static void cross_product(T*p, const T*x, const T*y) noexcept
    {
      p[0] = x[1]*y[2] - x[2]*y[1];
      p[1] = x[2]*y[0] - x[0]*y[2];
      p[2] = x[0]*y[1] - x[1]*y[0];
    }
  };
}

template<typename T, int Dimension>
struct point
{
   using helper = details::helper<Dimension>;
   T X[Dimension];  // for instance
   T operator*(point const&x) const noexcept { return helper::dot_product(X,x.X); }
   // etc.
};

template<typename T>
point<T,3> operator^(point<T,3> const&x, point<T,3> const&y) noexcept
{
  point<T,3> result;
  details::helper<3>::cross_product(result.X,x.X,y.X);
  return result;
}

Not sure it answer your question: 不确定它是否回答了您的问题:

You may remove inheritance, and use a member, so instead of: 您可以删除继承,并使用一个成员,所以不要:

template<class PointType> class NurbsCurve   : public Nurbs<1, PointType> { ... };
template<class PointType> class NurbsSurface : public Nurbs<2, PointType> { ... };
template<class PointType> class NurbsVolume  : public Nurbs<3, PointType> { ... };

Use something like: 使用类似:

template<class PointType> class NurbsCurve   { ... private: Nurbs<1, PointType> data; };
template<class PointType> class NurbsSurface { ... private: Nurbs<2, PointType> data; };
template<class PointType> class NurbsVolume  { ... private: Nurbs<3, PointType> data; };

Note: - You may have to copy prototype of Nurbs in each class. 注意:-您可能必须在每个课程中复制Nurbs的原型。 - Later if needed, you may replace Nurbs by a specific implementation. -以后如有需要,您可以通过特定的实现方式来替换Nurbs。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM