I have problems finding the right place for an actor and a timer used in a state machine.
I found some inspiration from this site about the state pattern: State Design Pattern in Modern C++ and created a small example:
There might be more transitions possible but I kept it short and simple.
class Door
{
void open() {}
void close() {}
};
Events:
class EventOpenDoor
{
public:
OpenDoor(Door* door) : m_pDoor(door) {}
Door* m_pDoor;
};
class EventOpenDoorTemporary
{
public:
EventOpenDoorTemporary(Door* door) : m_pDoor(door) {}
Door* m_pDoor;
};
class EventOpenDoorTimeout
{
public:
EventOpenDoorTimeout(Door* door) : m_pDoor(door) {}
Door* m_pDoor;
};
class EventCloseDoor
{
public:
EventCloseDoor(Door* door) : m_pDoor(door) {}
Door* m_pDoor;
};
using Event = std::variant<EventOpenDoor,
EventOpenDoorTemporary,
EventOpenDoorTimeout,
EventCloseDoor>;
States:
class StateClosed {};
class StateOpen {};
class StateTemporaryOpen {};
using State = std::variant<StateClosed,
StateOpen,
StateTemporaryOpen>;
Transitions (not complete):
struct Transitions {
std::optional<State> operator()(StateClosed &s, const EventOpenDoor &e) {
if (e.m_pDoor)
{
e.m_pDoor->open();
}
auto newState = StateOpen{};
return newState;
}
std::optional<State> operator()(StateClosed &s, const EventOpenDoorTemporary &e) {
if (e.m_pDoor)
{
e.m_pDoor->open();
**// start timer here?**
}
auto newState = StateOpen{};
return newState;
}
std::optional<State> operator()(StateTemporaryOpen &s, const EventOpenDoorTimeout &e) {
if (e.m_pDoor)
{
e.m_pDoor->close();
}
auto newState = StateOpen{};
return newState;
}
std::optional<State> operator()(StateTemporaryOpen &s, const EventOpenDoor &e) {
if (e.m_pDoor)
{
e.m_pDoor->open();
**// stop active timer here?**
}
auto newState = StateOpen{};
return newState;
}
/* --- default ---------------- */
template <typename State_t, typename Event_t>
std::optional<State> operator()(State_t &s, const Event_t &e) const {
// "Unknown transition!";
return std::nullopt;
}
};
Door controller:
template <typename StateVariant, typename EventVariant, typename Transitions>
class DoorController {
StateVariant m_curr_state;
void dispatch(const EventVariant &Event)
{
std::optional<StateVariant> new_state = visit(Transitions{this}, m_curr_state, Event);
if (new_state)
{
m_curr_state = *move(new_state);
}
}
public:
template <typename... Events>
void handle(Events... e)
{ (dispatch(e), ...); }
void setState(StateVariant s)
{
m_curr_state = s;
}
};
The events can be triggered by a client which holds an instance to the "DoorController"
door_controller->handle(EventOpenDoor{door*});
In the events I pass a pointer to the door itself so it's available in the transitions. The door is operated within the transitons only.
I have problems now with modelling the 20s timeout/timer. Where to have such a timer, which triggers the transition to close the door? Having a timer within the door instance means, I have a circular dependency, because in case of a timeout it needs to call "handle()" of the "door_controller".
I can break the circular dependency with a forward declarations. But is there a better solution?
Maybe I have modelled it not well. I'm open to improving suggetions. Thanks a lot!
Aucun commentaire:
Enregistrer un commentaire