I have been attempting to implement a hierarchical state machine in C++. I have chosen the approach based on the State design pattern and the Composite design pattern. According to the Composite design pattern I have defined a common abstract base class for the atomic states (simple states) and the composite states (states consisting of state machines)
State.h
class State {
public:
virtual void notifyCatModeRequested(bool state) = 0;
virtual void notifyBatModeRequested(bool state) = 0;
virtual void notifyMainContactorsCloseRequested(bool state) = 0;
virtual void update() = 0;
};
StateAtomic.h
#include "State.h"
#include "StateComposite.h"
class StateAtomic : public State {
public:
StateAtomic(StateComposite* parent) : parent(parent) {}
void notifyCatModeRequested(bool state) {cat_mode_requested = state;}
void notifyBatModeRequested(bool state) {bat_mode_requested = state;}
void notifyMainContactorsCloseRequested(bool state) {main_contactors_close_requested = state;}
virtual void update() = 0;
protected:
StateComposite *parent;
bool cat_mode_requested;
bool bat_mode_requested;
bool main_contactors_close_requested;
};
StateComposite.h
class StateComposite : public State {
public:
StateComposite(StateComposite* parent) : parent(parent) {}
void notifyCatModeRequested(bool state) {active->notifyCatModeRequested(state);}
void notifyBatModeRequested(bool state) {active->notifyBatModeRequested(state);}
void notifyMainContactorsCloseRequested(bool state) {main_contactors_close_requested = state;}
void update() {active->update();}
void switchState(State * new_state) {active = new_state;}
protected:
StateComposite* parent;
State* active;
};
According to the State design pattern, I have defined a class for the individual states of the state machine
Ready.h
#include "StateAtomic.h"
class StateMachine;
class Ready : public StateAtomic {
public:
Ready(StateMachine *parent) : : StateAtomic(parent) {}
void update() {
std::cout << "Ready" << std::endl;
if (cat_mode_requested && main_contactors_close_requested) {
parent->switchState(& static_cast<StateMachine*>(parent)->cat);
std::cout << "Switch to cat." << std::endl;
} else if (bat_mode_requested && main_contactors_close_requested) {
parent->switchState(& static_cast<StateMachine*>(parent)->bat);
std::cout << "Switch to bat." << std::endl;
}
}
};
Then I have defined the top-level state machine
#include "StateComposite.h"
#include "StateAtomic.h"
#include "Ready.h"
#include <iostream>
class StateMachine : public StateComposite {
public:
StateMachine() : StateComposite(nullptr), ready(this) {
active = &ready;
}
void update() {active->update();}
private:
Ready ready;
Cat cat;
friend class Ready;
friend class Cat;
};
And a very simple application
main.cpp
#include "StateMachine.h"
#include <cstdlib>
using namespace std;
struct command {
bool cat_mode_requested;
bool bat_mode_requested;
bool main_contactors_close_requested;
};
int main(int argc, char** argv) {
StateMachine state_machine;
command active_command;
for (uint8_t i = 0; i < 3; i++) {
std::cout << "Cat mode requested?" << std::endl;
std::cin >> active_command.cat_mode_requested;
std::cout << "Bat mode requested?" << std::endl;
std::cin >> active_command.bat_mode_requested;
std::cout << "Main contactors close requested?" << std::endl;
std::cin >> active_command.main_contactors_close_requested;
state_machine.notifyCatModeRequested(active_command.cat_mode_requested);
state_machine.notifyBatModeRequested(active_command.bat_mode_requested);
state_machine.notifyMainContactorsCloseRequested(active_command.main_contactors_close_requested);
state_machine.update();
}
}
I have found that the following steps result in program crash:
- Invoking the
state_machine.update()
- Invoking
parent->switchState(& static_cast<StateMachine*>(parent)->cat)
inside theReady::update()
- Invoking the
state_machine.update()
The problem is obviously in this statement: parent->switchState(& static_cast<StateMachine*>(parent)->cat)
called from the Ready::update()
.
Unfortunately, I don't understand why. Can anybody help me understand what I am doing wrong? Thanks in advance.
Aucun commentaire:
Enregistrer un commentaire