簡體   English   中英

在運行時選擇要使用的CRTP實現

[英]Choose at runtime which CRTP implementation to use

我想使用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();
    }
  }
};

請注意,函數Model::solve()執行大量迭代,並且我的應用程序中的性能至關重要,因此使用CRTP而不是動態多態來避免虛函數調用並啟用編譯器內聯函數。

現在,當我想讓用戶決定在運行時使用哪個FooInterfaceBarInterface實現時,我的問題出現了。 在我的main.cpp我有:

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();
}

我考慮過一種可以返回正確Model的工廠,但我不知道返回類型是什么,我想象的方式不方便,因為在我的應用程序中我有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;
    }
  }
};

我想象的方式,所有模板組合都將被編譯,用戶可以在運行時選擇使用哪一個。 有沒有辦法使用CRTP實現這一目標?

至於工廠方法,我認為沒有辦法定義單一類型,因為在編譯期間需要類型信息,實際設置只在程序執行期間才知道。

但是,如果您使用變體,則可以將所有可能的返回類型組合到一個變量中。 然后可以通過工廠方法返回此類型:

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()

住在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()

}

住在Coliru

您可以在轉換統一中的匿名命名空間中隱藏這些內容,您可以使用它們來更好地封裝並避免使用工廠。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM