C ++。模板和运行时用户信息

时间:2013-03-28 17:33:35

标签: c++ templates polymorphism numerical-integration

我一直在努力学习更多关于泛型编程的知识,因为这是我认为我还不够了解的东西。所以我在考虑如何实现我的某个程序的模板版本。 我试图这样做的程序是一个数值积分器程序,用户在其中选择使用哪个积分器(即Euler,Runge Kutta等),然后集成他们选择的任何函数。 我目前的这种方法是通过一个名为Integrator的抽象基类和几个实现集成方法的派生类。 所以代码看起来像这样(更多的是继续,但这只是为了显示方法)。请注意,我使用Qt,并声明了Integrator *集成器;在MainWindow类中。

void MainWindow::on_integrateButton_clicked() {
string whichIntegrator = getUserChoice();

integrator = getIntegrator( whichIntegrator, whichFunction, order );
integrator->setUp( data ); // things like initial conditions, time, step size, etc...

runIntegratorInNewThread();
}

getIntegrator基本上使用工厂方法

// x being current data, xdot being the results of evaluating derivatives
typedef void (*pFunction)(const double t, const double x[], double *xdot);

Integrator* getIntegrator( const string &whichIntegrator, pFunction whichFunction, int order  ) {
    if (whichIntegrator == "Euler") {
        return new Euler(whichFunction, order);
    } else if (whichIntegrator == "RungeKutta") {
        return new RungeKutta(whichFunction, order);
    }
}

所以这个方法工作正常,积分程序运行得很好。现在我知道模板函数是在编译时生成的,并且鉴于我正在使用运行时信息,你将如何使用模板实现它?如果问题不明确,那么我要问的是......在运行时选择用户,即使用哪个集成商,如何使用模板方法调用正确的集成函数?

2 个答案:

答案 0 :(得分:1)

模板不是灵丹妙药,虽然你可以用它们做很多事情,但不要忽视你目前使用的多态性的力量。

可以使用模板完成吗?答案是肯定的,它看起来像是使用C ++ 11和shared_ptr:

template<class T>
std::shared_ptr<T> getIntegrator(pFunction whichFunction, int order)
{
    return std::make_shared<T>(whichFunction, order);
}

在你的来电者中:

std::shared_ptr<Integrator> integrator;
if (whichIntegrator  == "Euler")
{
    integrator = getIntegrator<Euler>(whichFunction, order);
}
else if(whichIntegrator  == "RungeKutta")
{
    integrator = getIntegrator<RungeKutta>(whichFunction, order);
}

另外一个注意事项是,你应该对这里的记忆泄漏非常小心,你正在新手并反对,如果你永远不会释放它,你就会有泄漏。

所有这一切,我希望这个答案表明虽然你可以使用模板,但我不建议在这种情况下,多态性在这里运作良好。 此示例仅以极其简单且冗余的方式显示模板

答案 1 :(得分:1)

假设我想用静态类型的积分器编写整个系统。

我会带你的on_integrateButton_clicked,并将其更改为:

void MainWindow::on_integrateButton_clicked() {
  string whichIntegrator = getUserChoice();

  runInNewThread( [whichIntegrator,whichFunction,order]() {
    struct functor {
      FunctionType func;
      OrderType order;
      functor( FunctionType func_in, OrderType order_in):func(std::move(func_in)), order(std::move(order_in)) {}
      template<typename Integrator>
      void operator()( Integrator* integrator ) {
        // code using integrator here, not a virtual interface to it, an actual instance of the final type
      }
    };
    RunWithChosenIntegrator( whichIntegrator, functor(whichFunction,order) );
  } );
}

正如您所看到的,代码似乎有点倒退。

我们尽可能地推迟类型选择,此时我们用一个指向积分器的指针调用functor。这意味着使用积分器的代码具有积分器的完整类型信息,而不是抽象地处理它。

然而,通常,运行时多态或类型擦除足以解决这些问题。

现在,RunWithChosenIntegrator有点奇怪的野兽。它会有这样的标志性外观:

template<typename Functor>
void RunWithChosenIntegrator( std::string const&whichIntegrator, Functor&& func ) {
  if (whichIntegrator == "bob") {
    BobIntegrator bob;
    func( &bob );
  } else if (whichIntegrator == "alice" ) {
    AliceIntegrator alice;
    func( &alice ):
  }
}

如您所见,我们根据func参数使用不同类型的对象调用whichIntegrator。有一些有趣的方法甚至可以使用元编程生成if / else if链,但在您更熟悉基本模板编程之前,这可能不值得学习。

Functor func需要能够接受指向我们称之为的任何和所有类型的指针。一个简单的例子可能是func,它只是一个指向基类的指针,而我上面给出的那个采用T*模板类型。

如果运行时多态性的开销太高,或者实际上需要以非统一的方式与不同的集成器类进行交互,而这些方法很难或无法通过运行时多态性捕获,那么这一切都值得做。< / p>

我怀疑这是这种情况。