Factory Pattern for Template Classes and Default Argument of New Type for Template Classes
我有三层类,例如BaseSpreading(基类)、IterativeMapSpreading(BaseSpreading的一个子类)和TentSpreading、BernoullisSpreading(IterativeMapSpreading的两个子类)。一个名为GenerateSpreading的方法出现在所有类中。用户定义的选项指定分布,例如"帐篷"或"伯努利"。然后,if-else控制结构调用相应子类的generatespreading方法("帐篷"和"bernoulli"将分别调用tentspreading和bernoullispreading子类的generatespreading方法)。
我知道如何通过将generatespreading声明为virtual来解决我的问题,并定义一个基类指针(指向basespreading)。然后,可以根据用户选项在if-else控制结构内实例化派生类对象。尽管这样的实例在控制结构的作用域之外不可用,但是可以使用工厂模式将基类指针指向派生类对象,请参见本篇文章的示例。虽然基于虚拟方法的解决方案可以工作,但是由于它的性能不好,因此对于我的目的来说并没有用处。我的模拟调用了无数次虚拟函数。
作为一种替代方法,我使用模板编程。在这种情况下,我必须处理嵌套模板类的实例在if-else结构的范围之外不可用的问题。我的问题是,在使用模板类时是否可以扩展工厂模式思想。为了实现这一点,在可能的情况下,我必须创建一个指向模板基类的指针,然后将其指向实例化的模板子类,该子类在if-else结构中定义。然后问题归根结底就是为模板基类定义一个默认的模板参数,这一直是我的难题。此链接提供了一个示例,说明如何定义一个不是众所周知类型的默认模板参数,如"int"(链接示例的默认参数是向量的标准"分配器"模板)。
以下是我的脚本:
基底扩展.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | #ifndef BASE_SPREADING_H_ #define BASE_SPREADING_H_ #include <boost/filesystem.hpp> #include <boost/filesystem/fstream.hpp> #include <boost/numeric/ublas/vector.hpp> template <class S> class BaseSpreading { public: BaseSpreading(S& spreading); void generateSpreading(boost::numeric::ublas::vector<double>&); private: S& spreading_; }; template <class S> BaseSpreading<S>::BaseSpreading(S& spreading) : spreading_(spreading) {} template <class S> void BaseSpreading<S>::generateSpreading( boost::numeric::ublas::vector<double>& spr) { spreading_.generateSpreading(spr); } #endif /* BASE_SPREADING_H_ */ |
展开迭代图。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #ifndef SPREADING_ITERATIVE_MAP_H_ #define SPREADING_ITERATIVE_MAP_H_ #include <boost/numeric/ublas/vector.hpp> template <class S> class IterativeMapSpreading { public: IterativeMapSpreading(S& spreading); double evaluateMap(double); void generateSpreading(boost::numeric::ublas::vector<double>&); double sampleInitialPoint(); private: S& spreading_; void calculateFollowingPoints(boost::numeric::ublas::vector<double>&); }; template <class S> IterativeMapSpreading<S>::IterativeMapSpreading(S& spreading) : spreading_(spreading) {} template <class S> void IterativeMapSpreading<S>::calculateFollowingPoints( boost::numeric::ublas::vector<double>& spr) { for (unsigned int i=1; i<spr.size(); ++i) { spr(i) = spreading_.evaluateMap(spr(i-1)); } } template <class S> double IterativeMapSpreading<S>::evaluateMap(double x) { return spreading_.evaluateMap(x); } template <class S> void IterativeMapSpreading<S>::generateSpreading( boost::numeric::ublas::vector<double>& spr) { spr(0) = spreading_.sampleInitialPoint(); calculateFollowingPoints(spr); } template <class S> double IterativeMapSpreading<S>::sampleInitialPoint() { return spreading_.sampleInitialPoint(); } #endif /* SPREADING_ITERATIVE_MAP_H_ */ |
展开图H:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #ifndef SPREADING_TENT_H_ #define SPREADING_TENT_H_ #include <math.h> #include"random_number_generation.h" class TentSpreading { public: TentSpreading(double uniformMin=0, double uniformMax=1, double nonCentrality=0.5); double evaluateMap(double); double sampleInitialPoint(); private: const double uniformMin_, uniformMax_, nonCentrality_; double leftIntercept_, leftSlope_, rightIntercept_, rightSlope_; boost::random::uniform_real_distribution<> Uniform; void setLines(); void validateParameters() const; }; #endif /* SPREADING_TENT_H_ */ |
摊铺CPP:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | #include"spreading_tent.h" TentSpreading::TentSpreading(double uniformMin, double uniformMax, double nonCentrality) : uniformMin_(uniformMin), uniformMax_(uniformMax), nonCentrality_(nonCentrality), Uniform(uniformMin, uniformMax) { setLines(); } double TentSpreading::evaluateMap(double x) { double y; if((uniformMin_<=x) && (x<nonCentrality_)) y = leftSlope_*x+leftIntercept_; else if((nonCentrality_<=x) && (x<=uniformMax_)) y = rightSlope_*x+rightIntercept_; return y; } double TentSpreading::sampleInitialPoint() { return Uniform(rng); } void TentSpreading::setLines() { leftSlope_ = (uniformMax_-uniformMin_)/(nonCentrality_-uniformMin_); leftIntercept_ = -uniformMin_*(uniformMax_-nonCentrality_)/ (nonCentrality_-uniformMin_); rightSlope_ = -(uniformMax_-uniformMin_)/(uniformMax_-nonCentrality_); rightIntercept_ = (pow(uniformMax_, 2)-uniformMin_*nonCentrality_)/ (uniformMax_-nonCentrality_); } |
最后,main.cpp中感兴趣的部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | try { if (sbcOptions.mode=="sim-spr") { boost::numeric::ublas::vector<double> sprVector(3); if (sbcOptions.spr=="tent") { TentSpreading tent(-1, 1, 0); IterativeMapSpreading<TentSpreading> map(tent); BaseSpreading<IterativeMapSpreading<TentSpreading> > spreading(map); } spreading.generateSpreading(sprVector); } } catch(std::logic_error& logicError) { logTee << logicError.what() <<" "; return 1; } |
为了使用工厂模式,我尝试添加一个抽象的基类,称为FactoryBaseSpreading,其中BaseSpreading是一个子类。这就是工厂基础扩展的定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | FactoryBaseSpreading { public: static BaseSpreading* create(std::string type); } FactoryBaseSpreading* FactoryBaseSpreading::create(std::string type) { if (type =="tent") { TentSpreading tent(-1, 1, 0); IterativeMapSpreading<TentSpreading> map(tent); return new BaseSpreading<IterativeMapSpreading<TentSpreading> >(map); } return NULL; } //Also, change the following line in base_spreading.h: class BaseSpreading : public FactoryBaseSpreading |
这样,我就可以在"main.cpp"中定义一个指向FactoryBaseSpreading的指针,并将其指向if-else结构内的适当子类,例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | FactoryBaseSpreading* spreading; try { if (sbcOptions.mode=="sim-spr") { boost::numeric::ublas::vector<double> sprVector(3); if (sbcOptions.spr=="tent") { spreading = FactoryBaseSpreading::create("tent"); } spreading.generateSpreading(sprVector); } } catch(std::logic_error& logicError) { logTee << logicError.what() <<" "; return 1; } |
如果我在FactoryBaseSpreading中定义了一个虚拟的GenerateSpreading方法,那么它编译并运行正常。然而,我试图避免的是使用虚拟功能,所以工厂模式并不能解决我的问题。
因此,我考虑在BaseSpreading基类中定义"create"方法,而不创建FactoryBaseSpreading抽象基类。在这种情况下,"创建"方法的定义为:
1 2 3 4 5 6 7 8 9 | template <class S> BaseSpreading<S>* BaseSpreading<S>::create(std::string type) { if (type =="tent") { TentSpreading tent(-1, 1, 0); IterativeMapSpreading<TentSpreading> map(tent); return new BaseSpreading<IterativeMapSpreading<TentSpreading> >(map); } return NULL; } |
此代码的问题是,我无法再定义基类指针,因为它依赖于模板参数s。例如,这是无效的:
1 | BaseSpreading<S>* spreading; |
这就是为什么我要问我是否可以在basespreading的定义中提供一个默认参数。即使我能够指定默认的模板参数,我也无法在运行时切换模板参数,因为模板是由编译器在编译时实例化的,如本文所述。所以,我似乎不能避免使用虚拟函数,即使在模板元编程的帮助下也是如此。我唯一能想到的解决这个问题的方法是通过函数指针,委托函数,或者使用函数编程完全改变代码的设计…
如果我正确理解了所有这些,那么您需要基于您的内部循环中不会改变的内容进行动态调度,并且您希望内部循环不包含任何"if"语句或间接调度。这可以通过对代码进行少量的重组来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | template<typename SPREADING> void inner_loop(SPREADING* spreading) { while(something_long) { spreading->whatever(); } } main(){ if (opts.tentspreading) { TentSpreading spreading(stuff); inner_loop(&spreading); } else { IterativeMapSpreading spreading(other,stuff); inner_loop(&spreading); } } |
请注意,不能统一两个inner_loop(&;spreading)语句。实际上,它们使用不同的参数调用不同的函数。