samedi 19 avril 2014

C++ Template Design Question - débordement de pile


I have a model written in C++ which is based on run-time polymorphism and virtual functions. The model works completely fine as is. I'd like to investigate converting this to templates and compile-time polymorphism. The reasons are that:



  1. Only one class in the model is truly dynamic at run-time, the rest are compile-time decisions. The polymorphism is purely to make changes in the model easier and more flexible.

  2. I would like to inline several small functions in the model, which is impossible with virtual functions.


Here is a simplified hypothetical example of what my model looks like now:


class Particle
{
public:
// ...
virtual void move();
};

class Electron : public Particle { /*...*/ };


// Physics Models /////////////////////////////////

class PhysicsModel
{
public:
// ...
virtual void doStuffWithParticles();
private:
Particle* theParticle;
};

class NewtonPhysics : public PhysicsModel { /*...*/ };
class QuantumPhysics : public PhysicsModel { /*...*/ };


// SimulationModels ////////////////////////////

class SimulationModel
{
public:
virtual void runSimulation();
// ...
private:
PhysicsModel* thePhysics;
Particle* theParticle;
};

class HadronCollider : SimulationModel { /*...*/ };

But say if I want to inline the Particle functions, like move(), because it's the inner loop of a series of for-loops and could greatly benefit from the speed bonus. And at compile-time, I know what physics model and simulation model I'm running, but the Particle is a run-time decision.


So, how about:


template <typename TParticleType>
class PhysicsModel
{
// ...
TParticleType theParticle;
};

template <typename TParticleType,
typename TPhysicsModelType>
class SimulationModel
{
TParticleType theParticle;
TPhysicsModelType theModel;
};

Okay, so far so good. But now lets say at compile time I've decided we're using running the Hadron Simulation, with Quantum Physics model, and we're reading in an input file of particles. I want to avoid something of this flavor:


int main()
{
// ...
switch( getUserInput()->currentParticleType )
{
case ELECTRON: HadronSimulation<Electron, QuantumPhysics>.run();
case PROTON: HadronSimulation<Proton, QuantumPhysics>.run();
// ...
}
}

... and would rather put the "QuantumPhysics" type somewhere else so it's just in the same place. This is an oversimplified example, and actually I would have about 4-5 parameters which are compile-time decisions.


Basically I'm looking for a design that allows me to fit in a run-time parameter into this sort of template framework. Is there a good way to do this? Apologize if this seems like an idiotic question, but I'm completely new to templates in C++. Thanks in advance.




Static and dynamic polymorphism do not mix well, and where they interact with one another, some form of branching is required to turn run-time information into a choice among statically configured variants. You could use a simple type generator to at least reduce the repetition of your physics model:


template< class Particle >
class HadronSimulationGenerator {
public:
typedef HadronSimulation< Particle, QuantumPhysics > type;
};

int main()
{
// ...
switch( getUserInput()->currentParticleType )
{
case ELECTRON: HadronSimulationGenerator<Electron>::type.run();
case PROTON: HadronSimulationGenerator<Proton>::type.run();
// ...
}
}

Not much prettier.




How did you fix your PhysicsModel and ParticleType in the first example (without templates)? Was the constructor responsible for instantiating both? If yes, then you can use a similar technique to hide this from the user by doing your switch in the constructor of SimulationModel.


class SimulationModel {
void run() {
switch(currentPhysicsMode) {
case QUANTUM:
switch(currentParticleType) {
case ELECTRON:
SimulationModelImpl<Electron, QuantumPhysics>::run();
break;
case PROTON:
SimulationModelImpl<Proton, QuantumPhysics>::run();
break;
...
}
}
}
};

template <typename TParticleType,
typename TPhysicsModelType>
class SimulationModelImpl {
TParticleType theParticle;
TPhysicsModelType theModel;
...
};


I have a model written in C++ which is based on run-time polymorphism and virtual functions. The model works completely fine as is. I'd like to investigate converting this to templates and compile-time polymorphism. The reasons are that:



  1. Only one class in the model is truly dynamic at run-time, the rest are compile-time decisions. The polymorphism is purely to make changes in the model easier and more flexible.

  2. I would like to inline several small functions in the model, which is impossible with virtual functions.


Here is a simplified hypothetical example of what my model looks like now:


class Particle
{
public:
// ...
virtual void move();
};

class Electron : public Particle { /*...*/ };


// Physics Models /////////////////////////////////

class PhysicsModel
{
public:
// ...
virtual void doStuffWithParticles();
private:
Particle* theParticle;
};

class NewtonPhysics : public PhysicsModel { /*...*/ };
class QuantumPhysics : public PhysicsModel { /*...*/ };


// SimulationModels ////////////////////////////

class SimulationModel
{
public:
virtual void runSimulation();
// ...
private:
PhysicsModel* thePhysics;
Particle* theParticle;
};

class HadronCollider : SimulationModel { /*...*/ };

But say if I want to inline the Particle functions, like move(), because it's the inner loop of a series of for-loops and could greatly benefit from the speed bonus. And at compile-time, I know what physics model and simulation model I'm running, but the Particle is a run-time decision.


So, how about:


template <typename TParticleType>
class PhysicsModel
{
// ...
TParticleType theParticle;
};

template <typename TParticleType,
typename TPhysicsModelType>
class SimulationModel
{
TParticleType theParticle;
TPhysicsModelType theModel;
};

Okay, so far so good. But now lets say at compile time I've decided we're using running the Hadron Simulation, with Quantum Physics model, and we're reading in an input file of particles. I want to avoid something of this flavor:


int main()
{
// ...
switch( getUserInput()->currentParticleType )
{
case ELECTRON: HadronSimulation<Electron, QuantumPhysics>.run();
case PROTON: HadronSimulation<Proton, QuantumPhysics>.run();
// ...
}
}

... and would rather put the "QuantumPhysics" type somewhere else so it's just in the same place. This is an oversimplified example, and actually I would have about 4-5 parameters which are compile-time decisions.


Basically I'm looking for a design that allows me to fit in a run-time parameter into this sort of template framework. Is there a good way to do this? Apologize if this seems like an idiotic question, but I'm completely new to templates in C++. Thanks in advance.



Static and dynamic polymorphism do not mix well, and where they interact with one another, some form of branching is required to turn run-time information into a choice among statically configured variants. You could use a simple type generator to at least reduce the repetition of your physics model:


template< class Particle >
class HadronSimulationGenerator {
public:
typedef HadronSimulation< Particle, QuantumPhysics > type;
};

int main()
{
// ...
switch( getUserInput()->currentParticleType )
{
case ELECTRON: HadronSimulationGenerator<Electron>::type.run();
case PROTON: HadronSimulationGenerator<Proton>::type.run();
// ...
}
}

Not much prettier.



How did you fix your PhysicsModel and ParticleType in the first example (without templates)? Was the constructor responsible for instantiating both? If yes, then you can use a similar technique to hide this from the user by doing your switch in the constructor of SimulationModel.


class SimulationModel {
void run() {
switch(currentPhysicsMode) {
case QUANTUM:
switch(currentParticleType) {
case ELECTRON:
SimulationModelImpl<Electron, QuantumPhysics>::run();
break;
case PROTON:
SimulationModelImpl<Proton, QuantumPhysics>::run();
break;
...
}
}
}
};

template <typename TParticleType,
typename TPhysicsModelType>
class SimulationModelImpl {
TParticleType theParticle;
TPhysicsModelType theModel;
...
};

0 commentaires:

Enregistrer un commentaire