c++ - 模板基类实现一个默认实现

标签 c++ templates c++14 fsm

我正在尝试制作一个简单的模板化 FSM 库,它允许:machine.react(Event1{});这将调用实现 SomeState.react(std::shared_ptr<StateMachine> machine, Event1 event)无需提前知道所有可能的事件,无需为所有派生类实现所有和每个可能的事件。

当我从存储为 State 的 current_state_ 调用函数时类型,当调用 react(不是虚拟的)时,它将调用 Base 实现而不是 Derived (State1) 类实现。

#include <iostream>
#include <memory>
#include <tuple>
#include <typeinfo>
#include <vector>

template <typename ContextT, typename... StatesT>
class MachineT : public std::enable_shared_from_this<MachineT<ContextT, StatesT...>> {
  using M_ = MachineT<ContextT, StatesT...>;

 public:
  using Context = ContextT;
  class State {
   public:
    template <typename EVENT>
    void react(std::shared_ptr<M_> machine, EVENT event) {
      std::cout << "State::react" << std::endl;
    };
    virtual void update(std::shared_ptr<M_> machine) = 0;
  };

  MachineT(std::shared_ptr<ContextT> context)
      : context_(context),
        states_{std::make_shared<StatesT>()...} {
    using FirstEntityType = std::tuple_element_t<0, std::tuple<StatesT...>>;
    set<FirstEntityType>();
  }

  ~MachineT() {}

  template <typename T>
  std::shared_ptr<T> get() {
    return std::get<std::shared_ptr<T>>(states_);
  }

  template <typename T>
  void set() {
    current_state_ = std::get<std::shared_ptr<T>>(states_);
  }

  std::shared_ptr<M_> shared() {
    return this->shared_from_this();
  }

  void update(){
    current_state_->update(shared());
  };

  template <typename EVENT>
  void react(EVENT event){
    current_state_->react(shared(), event);
  };

 private:
  std::shared_ptr<ContextT>               context_;
  std::tuple<std::shared_ptr<StatesT>...> states_;
  std::shared_ptr<State>                  current_state_;
};

struct State1;
struct State2;
struct Context {};
struct Event1 {};

using StateMachine = MachineT<Context, State1, State2>;


struct State1 : public StateMachine::State {
  int  value = 10;
  void print() { std::cout << value << std::endl; }

  void react(std::shared_ptr<StateMachine> machine, Event1 event) {
    std::cout << "State1::react()" << std::endl;
  }
  void update(std::shared_ptr<StateMachine> machine) {
    std::cout << "State1::update()" << std::endl;
  }
};

struct State2 : public StateMachine::State {
  int  value = 20;
  void print() { std::cout << value << std::endl; }
  void react(std::shared_ptr<StateMachine> machine, Event1 event) {
    std::cout << "State2::react()" << std::endl;
  }
  void update(std::shared_ptr<StateMachine> machine) {
    std::cout << "State2::update()" << std::endl;
  }
};

int main() {
  std::shared_ptr<Context> context;
  context.reset(new Context());
  std::shared_ptr<StateMachine> machine;
  machine.reset(new StateMachine(context));
  machine->set<State1>();
  machine->update();
  machine->react(Event1{});
  machine->set<State2>();
  machine->update();
  machine->react(Event1{});
}

完整示例:http://cpp.sh/7d37rq

当前输出:

State1::update()
State::react()
State2::update()
State::react()

预期输出:

State1::update()
State1::react()
State2::update()
State2::react()

最佳答案

为此你需要 std::variant 并将当前状态存储为:

std::variant<std::shared_ptr<StatesT>...> current_state_;

具有检测给定状态是否对某些事件类型作出 react 的特征:

template <typename State, typename Event, typename = void>
struct can_react : std::false_type {};

template <typename State, typename Event>
struct can_react<State, Event,
    decltype(void(std::declval<State>()->react(nullptr, std::declval<Event>())))>
    : std::true_type {};

然后,如果状态可以处理,您可以将事件发送到状态,否则丢弃:

template <typename Event>
void react(Event event) {
    std::visit([&](auto state) {
        if constexpr (can_react<decltype(state), Event>{}) {
            state->react(shared(), event);
        }
    }, current_state_);
} 

同样,update 变成:

void update() {
    std::visit([this](auto state) {
        state->update(shared());
    }, current_state_);
}

DEMO


完整代码:

template <typename ContextT, typename... StatesT>
class MachineT : public std::enable_shared_from_this<MachineT<ContextT, StatesT...>> {
  using M_ = MachineT<ContextT, StatesT...>;

 public:
  using Context = ContextT;

  MachineT(std::shared_ptr<ContextT> context)
      : context_(context),
        states_{std::make_shared<StatesT>()...} {
    using FirstEntityType = std::tuple_element_t<0, std::tuple<StatesT...>>;
    set<FirstEntityType>();
  }

  ~MachineT() {}

  template <typename T>
  std::shared_ptr<T> get() {
    return std::get<std::shared_ptr<T>>(states_);
  }

  template <typename T>
  void set() {
    current_state_ = std::get<std::shared_ptr<T>>(states_);
  }

  std::shared_ptr<M_> shared() {
    return this->shared_from_this();
  }

  void update() {
    std::visit([this](auto state) { state->update(shared()); }, current_state_);
  }

  template <typename State, typename Event, typename = void>
  struct can_react : std::false_type {};

  template <typename State, typename Event>
  struct can_react<State, Event,
      decltype(void(std::declval<State>()->react(nullptr, std::declval<Event>())))>
      : std::true_type {};

  template <typename Event>
  void react(Event event) {
    std::visit([&](auto state) {
      if constexpr (can_react<decltype(state), Event>{}) {
        state->react(shared(), event);
      }
    }, current_state_);
  } 

 private:
  std::shared_ptr<ContextT>                 context_;
  std::tuple<std::shared_ptr<StatesT>...>   states_;
  std::variant<std::shared_ptr<StatesT>...> current_state_;
};

状态和事件:

struct State1;
struct State2;
struct Context {};
struct Event1 {};
struct Event2 {};

using StateMachine = MachineT<Context, State1, State2>;

struct State1 {    
  void react(std::shared_ptr<StateMachine> machine, Event1 event) {
    std::cout << "State1::react(Event1)" << std::endl;
  }

  void update(std::shared_ptr<StateMachine> machine) {
    std::cout << "State1::update()" << std::endl;
  }
};

struct State2 {            
  void react(std::shared_ptr<StateMachine> machine, Event2 event) {
    std::cout << "State2::react(Event2)" << std::endl;
  }

  void update(std::shared_ptr<StateMachine> machine) {
    std::cout << "State2::update()" << std::endl;
  }
};

测试:

int main() {
  std::shared_ptr<Context> context;
  context.reset(new Context());
  std::shared_ptr<StateMachine> machine;
  machine.reset(new StateMachine(context));
  machine->set<State1>();
  machine->update();
  machine->react(Event1{});
  machine->set<State2>();
  machine->update();
  machine->react(Event2{});
}

输出:

State1::update()
State1::react(Event1)
State2::update()
State2::react(Event2)

使用 这是possible使用 boost::variant :

template <typename Event>
struct Visitor {
    MachineT* machine;
    Event* event;
    template <typename State>
    std::enable_if_t<can_react<State, Event>{}> operator()(State state) const {
        state->react(machine->shared(), *event);
    }
    template <typename State>
    std::enable_if_t<!can_react<State, Event>{}> operator()(State) const {}
};

template <typename Event>
void react(Event event) {
    boost::apply_visitor(Visitor<Event>{ this, &event }, current_state_);
}

关于c++ - 模板基类实现一个默认实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62433784/

相关文章:

c++ - 可组合的 C++ 函数装饰器

c++ - 有没有类似于Python中的C++中split()的东西?

C++ QT SQLite 最佳实践

templates - 如何在以下商店结构中添加路径

c++ - 内置类型的模板参数默认值

c++ - 当没有 float 数据类型时,为什么这段代码会出现浮点异常?

c++ - std::errc,如何在 retval 中指示成功

c++ - 如果我尝试在同一个项目中使用 STL vector 和 CUDA 推力 vector ,为什么会出现链接错误?

c++ - Visual C++ 编译器为不明确的符号提供了错误的行引用

c++ - 内部类取决于模板参数