简体   繁体   中英

Changing template type of a variable at runtime

I am having an issue writing a state machine that would switch a template-based variable during the execution. Currently, the implementation is as follows:

template <typename TemplateT>
class InnerClass {
  void foo();
}

class StateMachine {
public:
  std::unique_ptr<InnerClass<ParticularType>> inner;

  void someMethod() {
    ...
    inner->foo();
    ...
  }
}

I need to generalize this implementation so that StateMachine would have a member that would be changing the inner variable to different template-based type:

void StateMachine::changeState(int state) {
  switch(state) {
  case 0:
    inner.reset(new InnerClass<ParticularType0>());
    break; 
  case 1:
    inner.reset(new InnerClass<ParticularType1>());
    break; 
  case 2:
    inner.reset(new InnerClass<ParticularType2>());
    break;
  ...
  default:
    assert();
  }
}

I know that templates can only be used when the type is known at compile-time, hence, most likely, there should be another approach that I am unaware of. Could you please point out some ideas that can be applied here? Are there any other approaches besides using the creation of a templateless interface to the InnerClass :

class InnerClassInterface {
  virtual void foo() = 0;
}

template <typename TemplateT>
class InnerClass : InnerClassInterface {
  virtual void foo() override;
}

Hence, most likely, there should be another approach that I am unaware of

Unfortunately, there isn't. Just like you said, templates are evaluated at compile-time. Meaning that unless you type out every possible type parameter for InnerClass , like you're doing in the switch statement, there would be no way for the compiler to know which instances of the InnerClass template to instantiate.

You would most likely be better off changing your design completely and using some form of polymorphism.

But if you really want to preserve this design, one solution could be to put the the different versions of ParticularType into specialized class templates. I have renamed ParticularType to State and changed the different versions of it to some state names since I assume the ParticularType s are meant to represent states.

// states.h
enum
{
    STATE_WALKING,
    STATE_RUNNING,
    STATE_SHOOTING
    ...
};
// state.h
class StateBase
{
public:
    // Insert what your states actually do here
    //        |
    //        V
    virtual void someStateFunc() = 0;
};

template<int I>
class State
{
};

template<>
class State<STATE_WALKING> : public StateBase
{
public:
    // Code for walking state
    void someStateFunc() override
    {
        ...
    }
};

template<>
class State<STATE_RUNNING> : public StateBase
{
public:
    //Code for running state
    void someStateFunc() override
    {
        ...
    }
};

template<>
class State<STATE_SHOOTING> : public StateBase
{
    // Code for shooting state
    ...
};

...

In the code in your example you declared inner as being of type std::unique_ptr<InnerClass<ParticularType>> . This would fail to compile since you try to assign unique_ptr s of type InnerClass<ParticularType0> and InnerClass<ParticularType1> to inner inside changeState . This is easy to fix though, you just need a base class for InnerState to inherit from.

Here's what InnerClass and StateMachine would look like:

class InnerClassBase
{
public:
    virtual void foo() = 0;
    ...
};

template<typename T>
class InnerClass : public InnerClassBase
{
public:
    void foo() override
    {
        ...
    }
};

class StateMachine {
public:
    std::unique_ptr<InnerClassBase> inner;

    void someMethod() {
      ...
      inner->foo();
      ...
    }

    template<int S>
    void changeState() {
        inner.reset(new InnerClass<State<S>>());
    }
};

and call it like so:

#include "states.h"

...

if(some_condition)
{
    stateMachine.changeState<STATE_SHOOTING>();
}
...

Every time you want to add a new state, you just add a new specialization to State .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM