c++ - 具有伪继承设计 C++ 的模板
问题描述
这个问题可能属于“想要世界上最好的”,但这是一个真正的设计问题,至少需要一个更好的解决方案。
按重要性排序,这是让我卡住的要求
- 我们需要模板,无论是在类还是函数级别。在这一点上,我们高度依赖于函数参数中的模板对象。因此,如果有任何东西离开下面的模型,它的虚函数(据我所知)。
- 我们希望将调用与选择分离。我们希望用户声明一个数学对象并让背景计算出来,最好是在运行时。
- 我们希望有一个默认值,如上图所示。
在我公司的程序中,我们有一个关键的算法生成器,它依赖于编译时和运行时的多态性,即模板类和虚拟继承。我们让它工作,但它很脆弱,难以阅读和开发,并且具有某些无法在更高优化级别上工作的特性(这意味着我们在某处依赖未定义的行为)。代码的简要概述如下。
// Math.hpp
#include <dataTypes.hpp>
// Base class. Actually handles CPU Version of execution
template <typename T>
class Math {
// ...
// Example function. Parameters vary in type and number
// Variable names commented out to avoid compile warnings
virtual void exFunc ( DataType<T> /*d*/, float /*f*/ )
{
ERROR_NEED_CODE; // Macro defined to throw error with message
}
// 50+ other functions...
};
//============================================================
// exampleFuncs.cpp
#include<Math.hpp>
template <> void Math<float>::exFunc ( DataType<float> d, float f)
{
// Code Here.
}
我们已经看到了一些问题,但我们还没有解决主要问题。由于此类中的函数数量众多,我们不想在头文件中定义所有函数。模板功能因此丢失。其次,对于带有模板类的虚函数,无论如何我们都需要在类中定义每个函数,但我们只是抛出一个错误并返回垃圾(如果需要返回)。
//============================================================
// GpuMath.hpp
#include <Math.hpp>
// Derived class. Using CUDA to resolve same math issues
GpuMath_F : Math<float> { ... };
这里的功能相对简单,但我再次注意到,我们放弃了模板功能。我不确定它是否需要这样,但以前的开发人员觉得必须为每种需要的类型声明一个新类(目前是 3 个。乘以 50 个左右的函数,我们有严重的开销)。
最后,当需要功能时。我们使用Factory来创建正确的模板类型对象并将其存储在 Math 指针中。
// Some other class, normally template
template <typename T>
class OtherObject {
Math<T>* math_;
OtherObject() {
math_ = Factory::get().template createMath<T> ();
// ...
}
// ...
};
这里省略了工厂。它变得混乱,对我们没有多大帮助。关键是我们将所有版本的数学对象存储在基类中。
您能否为我指出替代继承的其他技术的正确方向?我在寻找策略设计的变体吗?有模板技巧吗?
感谢您的阅读,并提前感谢您的意见。
解决方案
正如之前多次讨论过的,具有虚拟功能的模板不能很好地结合在一起。最好选择其中一个。
方法 1:助手类
到目前为止,我们拥有的第一个也是最好的选择就是这样做的,即为包装类选择不使用虚拟功能。
class MathHelper {
Math cpuMath;
GpuMath gpuMath;
bool cuda_; //True if gpuMath is wanted
template <typename T>
void exFunc ( DataType<T> d, float f )
{
if (cuda_)
gpuMath.exFunc( d, f );
else
cpuMath.exFunc( d, f );
}
// 50+ functions...
};
首先,您可能已经注意到函数是模板化的,而不是类。它在结构上更方便。
优点
- 获得对 CPU 和 GPU 类中模板的完全访问权限。
- 改进了每个功能的自定义。选择什么是默认的。
- 对先前结构的非侵入性更改。例如,如果这个 MathHelper 刚刚被调用
Math
,并且我们有CpuMath
和GpuMath
作为实现,实例化和使用几乎可以与上面相同,如果我们让 Factory 处理 MathHelper,则保持完全相同。
缺点
- 显式 if/else 和每个函数的声明。
- MathHelper 中每个函数的强制定义以及至少一个其他 Math 对象。
- 结果,到处都是重复的代码。
方法二:宏观
这个尝试减少上面的重复代码。在某个地方,我们有一个数学函数。
class Math {
CpuMath cpuMath;
GpuMath gpuMath;
// Some sort of constructor
static Math& math() { /*static getter*/ }
};
此数学助手使用类似于此处所示的考试 1的静态 getter 函数。我们有CpuMath
不包含虚函数的基类和派生类GpuMath
。同样,模板是在功能级别上的。
然后从那里,任何时候我们想要一个数学函数,我们都使用这个宏:
#define MATH (func, ret...) \
do { \
if (math.cuda_) \
ret __VA_OPT__(=) math().cuda.func; \
else \
ret __VA_OPT__(=) math().cpu.func; \
} while (0)
优点
- 删除前一个包装器的重复代码。
- 再次解锁模板的全部功能
缺点
- 不像上面的包装器那样可定制
- 最初更具侵入性。每次访问 Math 函数时,它都必须从
val = math_.myFunc(...)
, 变为MATH (myFunc(...), val)
。由于编辑器没有对宏进行良好的错误检查,这可能会在编辑过程中导致许多错误。 - 基类必须具有派生类的每个函数,因为它是默认的。
同样,如果有任何其他创造性的方式来实现这个设计,我们将不胜感激。无论哪种方式,我都觉得这是一个有趣的练习,并且很想继续从中学习。
推荐阅读
- javascript - react hooks 可以完全替代 redux 吗?
- python - 使用 Python 和 selenium 抓取 URL
- c# - 如何删除列表视图 MVVM 中的行?
- python - 在单个 matplotlib 图中多次使用颜色循环器
- git - Github:主页搜索栏...单击搜索栏时如何删除自动弹出的存储库
- python - 根据另一列中的值填充熊猫数据框中的缺失数据
- reactjs - Apollo GraphQL - 订阅响应中不包含相关类型
- javascript - css网格填充100vh而不影响行高
- javascript - 在获取数据时使用 useEffect 和 useContext
- tsql - 为给定的 fileID 选择具有最大 orderID 的记录的详细信息