2017-06-30 13 views
3

CRTPイディオムを使用して静的多型を使用し、実行時に使用する実装を選択できるようにします。私は例を見てみましょう:実行時に使用するCRTP実装を選択する

私はものを計算するための責任がいくつかのクラスがありますのは、それを呼びましょう、今、私は別のクラスのメンバーとして、これらのオブジェクトを使用したい

template<typename Implementation> 
class FooInterface { 
public: 
    void compute(){ 
    (static_cast<Implementation*>(this))->compute(); 
    } 
}; 

class FooForward : public FooInterface<FooForward> { 
public: 
    void compute(){ 
    //do stuff 
    } 
}; 

class FooBackward : public FooInterface<FooBackward> { 
public: 
    void compute(){ 
    //do other stuff 
    } 
}; 

template<typename Implementation> 
class BarInterface { 
public: 
    void eval(){ 
    (static_cast<Implementation*>(this))->eval(); 
    } 
}; 

class BarForward : public BarInterface<BarForward> { 
public: 
    void eval(){ 
    //do something 
    } 
}; 

class BarBackward : public BarInterface<BarBackward> { 
public: 
    void eval(){ 
    //do something else 
    } 
}; 

Model、およびループ内でそれらを使用する:

template<typename Foo, typename Bar> 
class Model { 
private: 
    Foo* foo_; 
    Bar* bar_; 
    int max_iter_; 

public: 
    Model<Foo, Bar>(int max_iter) : max_iter_(max_iter){ 
    foo_ = new Foo(); 
    bar_ = new Bar(); 
    } 

    void solve(){ 
    for(int i = 0; i < max_iter_; ++i){ 
     foo_->compute(); 
     bar_->eval(); 
    } 
    } 
}; 

注意をFUNCTことイオンModel::solve()は高い反復回数を実行し、アプリケーションでのパフォーマンスは非常に重要です。したがって、動的な多態性の代わりにCRTPを使用して、仮想関数呼び出しを回避し、コンパイラによる関数のインライン化を有効にします。

私の問題は、実行時にFooInterfaceBarInterfaceのどちらの実装を使用するかをユーザーに決定させたいときに発生します。私のmain.cppでは私が持っている:私は正しいModelを返すことができますが、私は戻り値の型は何ができるか分からない、と私は想像する方法は、それが原因で便利ではありません工場の種類について考えている

int main(int argc, char** argv){ 
    /* 
    * Here an input file is read into a map which looks like this 
    * std::map<std::string, std::string> settings 
    */ 
    // Here I need a way to choose, based on settings, what will Foo and Bar be 
    Model<Foo, Bar> model; 
    model.solve(); 
} 

を私のアプリケーションでは、私は2つの以上のテンプレートパラメータを持って、その後、組み合わせの数は、私は物事を想像する方法非常に大きな

class Factory{ 
    /*type?*/ createModel(std::map<std::string, std::string> settings){ 
    if ((settings["foo"] == "fwd") && (settings["bar"] == "fwd")){ 
     Model<FooForward, BarForward>* model = new Model<FooForward, BarForward>(); 
     return model; 
    } 
    else if ((settings["foo"] == "fwd") && (settings["bar"] == "bwd")){ 
     Model<FooForward, BarBackward>* model = new Model<FooForward, BarBackward>(); 
     return model; 
    } 
    else if ((settings["foo"] == "bwd") && (settings["bar"] == "fwd")){ 
     Model<FooBackward, BarForward>* model = new Model<FooBackward, BarForward>(); 
     return model; 
    } 
    else { 
     Model<FooBackward, BarBackward>* model = new Model<FooBackward, BarBackward>(); 
     return model; 
    } 
    } 
}; 

となり、すべてのテンプレートの組み合わせがコンパイルされるだろうし、ユーザーが1つが使用する実行時に選択することができます。 CRTPを使ってこれを達成する方法はありますか?

答えて

4

ファクトリメソッドに関しては、コンパイル時に型情報が必要となり、実際の設定はプログラムの実行中にのみ認識されるため、単一の型を定義する方法はないと思います。

ただし、バリアントを使用する場合は、すべての可能なリターンタイプを1つにまとめることができます。このタイプは、ファクトリメソッドによって返されます。

class Factory{ 

public: 

    using ModelVariant = boost::variant 
    < 
     Model< FooBackward , BarBackward > , 
     Model< FooBackward , BarForward > , 
     Model< FooForward , BarBackward > , 
     Model< FooForward , BarForward > 
    >; 

    static ModelVariant createModel(std::map<std::string, std::string> settings , int i) 
    { 
     if ((settings["foo"] == "fwd") && (settings["bar"] == "fwd")){ 
      Model<FooForward, BarForward> model = Model<FooForward, BarForward>(i); 
      return model; 
     } 
     else if ((settings["foo"] == "fwd") && (settings["bar"] == "bwd")){ 
      Model<FooForward, BarBackward> model = Model<FooForward, BarBackward>(i); 
      return model; 
     } 
     else if ((settings["foo"] == "bwd") && (settings["bar"] == "fwd")){ 
      Model<FooBackward, BarForward> model = Model<FooBackward, BarForward>(i); 
      return model; 
     } 
     else// ((settings["foo"] == "bwd") && (settings["bar"] == "bwd")) 
     { 
      Model<FooBackward, BarBackward> model = Model<FooBackward, BarBackward>(i); 
      return model; 
     } 
    } 
}; 

しかし、今、あなたは実際に必要solve()方法を呼び出すために訪問者が必要になります:に頼ることなく、また

auto model { Factory::createModel(settings , 1) }; 

boost::apply_visitor([ ](auto & m){ m.solve(); } , model); 
//  > FooForward::compute() 
//  > BarBackward::eval() 

Live at Coliru

を異なるFooBarの実装を別々に追加し続ければ、これをすぐにメタプログラミングすることが難しいでしょう。


オリジナルの答え:あなたはこれらの事を隠すことができ

std::map< std::string , std::string > settings 
{ 
    { "foo" , "fwd" } , 
    { "bar" , "bwd" } 
}; 

template< typename F , typename B> 
static Model< F , B > m(1); 

void solve() 
{ 
    if ((settings["foo"] == "fwd") && (settings["bar"] == "fwd")){ 
     m<FooForward, BarForward>.solve(); 
    } 
    else if ((settings["foo"] == "fwd") && (settings["bar"] == "bwd")){ 
     m<FooForward, BarBackward>.solve(); 
    } 
    else if ((settings["foo"] == "bwd") && (settings["bar"] == "fwd")){ 
     m<FooBackward, BarForward>.solve(); 
    } 
    else// ((settings["foo"] == "bwd") && (settings["bar"] == "bwd")) 
    { 
     m<FooBackward, BarBackward>.solve(); 
    } 
} 

int main() 
{ 
    // Load settings somehow 

    solve(); // > FooForward::compute() 
      // > BarBackward::eval() 

} 

Live at Coliru

はたぶん、あなたは、静的テンプレート変数と単純な関数を使用することができますあなたがそれらを使用する翻訳統一の匿名の名前空間より良いカプセル化と工場の使用を避けてください。

+0

ご回答ありがとうございます。私はこれを試してお知らせします。メンテナンス部分については、それは私が恐れていたものです。私は現在、 'Foo'と' Bar'では2つ以上の実装を持ち、 'Model'では2つ以上のメンバを持ち、可能な組み合わせの数を増やしています。ある種のテンプレートメタプログラミングがすべての組み合わせを処理するのに役立つと思うか、私のデザインを再考する方がいいですか? –

関連する問題