C++ 在运行时选择要使用的CRTP实现

C++ 在运行时选择要使用的CRTP实现,c++,crtp,C++,Crtp,我希望使用静态多态性来使用CRTP习惯用法,同时能够在运行时选择要使用的实现。让我举一个例子: 我有一些负责计算的课程: template<typename Implementation> class FooInterface { public: void compute(){ (static_cast<Implementation*>(this))->compute(); } }; class FooForward : public FooInt

我希望使用静态多态性来使用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
  }
};
请注意,函数
Model::solve()
执行大量迭代,在我的应用程序中性能至关重要,因此使用CRTP而不是动态多态性来避免虚拟函数调用,并启用编译器对函数的内联

现在,当我想让用户决定在运行时使用
FooInterface
BarInterface
的哪个实现时,我的问题就出现了。在my
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();
}

按照我的设想,所有的模板组合都将被编译,用户可以在运行时选择使用哪一个。有没有办法使用CRTP来实现这一点?

至于工厂方法,我认为没有办法定义单一类型,因为编译时需要类型信息,而实际设置只有在程序执行时才知道

但是,如果您使用一个变体,就可以将所有可能的返回类型组合成一个单一的返回类型。然后可以通过factory方法返回此类型:

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

此外,如果不借助某种模板元编程,那么如果您继续添加不同的
Foo
Bar
实现,很快就会很难实现这一点


原始答案:

也许您可以使用一个静态模板变量和一个简单的函数:

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

}
std::map设置
{
{“foo”,“fwd”},
{“bar”,“bwd”}
};
模板
静态模型m(1);
void solve()
{
如果((设置[“foo”]=“fwd”)&(设置[“bar”]=“fwd”)){
m、 solve();
}
否则如果((设置[“foo”]=“fwd”)&&(设置[“bar”]=“bwd”)){
m、 solve();
}
否则如果((设置[“foo”]=“bwd”)&&(设置[“bar”]=“fwd”)){
m、 solve();
}
else/((设置[“foo”]=“bwd”)&&(设置[“bar”]=“bwd”))
{
m、 solve();
}
}
int main()
{
//以某种方式加载设置
solve();/>foofroward::compute()
//>BarBackward::eval()
}


您可以将这些内容隐藏在translation unity中的匿名名称空间中,以便更好地进行封装,避免使用工厂。

非常感谢您的回答,我将尝试一下,并让您知道。至于维修部分,那是我担心的。我目前有两个以上的
Foo
Bar
实现,还有两个以上的
Model
成员,这大大增加了可能的组合数量。你认为某种模板元编程可以帮助我处理所有的组合,还是重新思考我的设计更好?
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;
    }
  }
};
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;
        }
    }
};
auto model { Factory::createModel( settings , 1 ) };

boost::apply_visitor( [ ]( auto & m ){ m.solve(); } , model );
//     > FooForward::compute()
//     > BarBackward::eval()
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()

}