BaseSpreading (基本クラス)、IterativeMapSpreading (BaseSpreading のサブクラスの 1 つ)、TentSpreading、BernoulliSpreading (IterativeMapSpreading のサブクラスの 2 つ) など、3 つのクラス層があります。すべてのクラスに、generateSpreadingというメソッドが表示されます。ユーザー定義のオプションは、「テント」や「ベルヌーイ」などの拡散を指定します。次に、if-else 制御構造が、適切なサブクラスの generateSpreading メソッドを呼び出します ("tent" と "bernoulli" は、それぞれ TentSpreading と BernoulliSpreading サブクラスの generateSpreading メソッドを呼び出します)。
generateSpreading を仮想として宣言し、基本クラス ポインター (BaseSpreading への) を定義することで問題を解決する方法を知っています。次に、ユーザー オプションに従って、派生クラス オブジェクトを if-else 制御構造内でインスタンス化できます。このようなインスタンスは制御構造のスコープ外では使用できないという事実にもかかわらず、ファクトリ パターンを使用して、基本クラス ポインターを派生クラス オブジェクトにポイントすることができます。たとえば、この投稿を参照してください。仮想メソッドに基づくソリューションは機能しますが、パフォーマンスが悪いため、私の目的には役に立ちません。私のシミュレーションは仮想関数を何百万回も呼び出します。
別のアプローチとして、テンプレート プログラミングを使用しました。この場合、ネストされたテンプレート クラスのインスタンスが if-else 構造のスコープ外では使用できないという問題に対処する必要があります。私の質問は、テンプレート クラスを操作するときにファクトリ パターンのアイデアを拡張できるかどうかです。これを機能させるには、可能であれば、テンプレートの基本クラスへのポインターを作成し、if-else 構造内で定義されているインスタンス化されたテンプレートのサブクラスを指すようにする必要があります。問題は、テンプレート基本クラスのデフォルトのテンプレート引数を定義することに要約されます。これが私の困難でした。このリンク「int」などのよく知られていないタイプのデフォルトのテンプレート引数を定義する方法の例を示します(リンクの例のデフォルトの引数は、ベクトルの標準の「アロケーター」テンプレートです)。
これらは私のスクリプトです:
base_spreading.h:
#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_ */
spread_iterative_map.h
#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_ */
spread_tent.h:
#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_ */
spread_tent.cpp:
#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 の部分:
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() << "\n";
return 1;
}
ファクトリ パターンを使用するために、FactoryBaseSpreading と呼ばれる抽象基本クラスを追加してみました。この BaseSpreading はそのサブクラスです。これは FactoryBaseSpreading の定義です。
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 構造内の適切なサブクラスを指すことができます。次に例を示します。
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() << "\n";
return 1;
}
FactoryBaseSpreading で仮想の generateSpreading メソッドを定義すれば、これは正常にコンパイルおよび実行されます。ただし、回避しようとしていたのは仮想関数の使用であったため、ファクトリ パターンでは問題は解決しません。
そこで、FactoryBaseSpreading 抽象基本クラスを作成せずに、BaseSpreading 基本クラスで「作成」メソッドを定義することを考えました。この場合、「作成」メソッドの定義は次のようになります。
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 に依存しているため、基本クラスのポインターを定義できないことです。たとえば、これは有効ではありません。
BaseSpreading<S>* spreading;
そのため、BaseSpreading の定義でデフォルトの引数を提供できるかどうかを尋ねています。ここ で説明されているように、テンプレートはコンパイル時にコンパイラによってインスタンス化されるため、デフォルトのテンプレート引数を指定できたとしても、実行時にテンプレート引数を切り替えることはできません。したがって、テンプレート メタプログラミングの助けを借りても、仮想関数の使用を避けることはできないようです。この問題に取り組むために考えられる他の唯一の方法は、関数ポインターを使用するか、関数を委任するか、関数型プログラミングを使用してコードの設計を完全に変更することです...