patterncppMinor
A variant of state pattern + C++ template FSM without state creation/destruction penalty
Viewed 0 times
destructionwithouttemplatevariantpenaltystatefsmpatterncreation
Problem
This is a variant of the solution posted here, without state creation/destruction penalty.
Note that my comment in the original question about the simplification provided by C++11's inherited constructors applies here too, but I have not updated the code in this question accordingly. That is left as an exercise to the reader.
The variant
I like the event/action syntax of the solution I proposed in my question. However, I really dislike the runtime penalty of creating/destroying states on the fly (I have made no measurement, but I dislike the principle).
I present hereafter a variant where the event/action syntax simplicity is pretty much kept (although slightly modified), but all states are created in advance. To keep my syntax, the states obviously need to be context-aware, which makes my solution anti-flyweight (one instance of every single concrete state per instance of a state machine). My feeling is that the cost in RAM is more affordable to the applications I have in mind than the runtime penalty. Another cost of this variant is a more complex declaration for states: declaration of state instances, need for a state to refer not only to the state machine but also to the nearest superstate.
Obviously, a straight application of the GoF's state pattern is an alternative for applications that need both flyweight and runtime efficiency (at the expense of event/action syntax simplicity).
Please comment on this.
genericstate.h
```
#ifndef GENERICSTATE_H
#define GENERICSTATE_H
template
class GenericState
{
public:
static void init(State *&state, State &initState) {
state = &initState;
initState.entry();
}
protected:
explicit GenericState(StateMachine &m, State *&state) :
m(m), s(m), state(state) {}
explicit GenericState(StateMachine &m, State *&state, SuperState &s) :
m(m), s(s), state(state) {}
void change(State &targetState) {
exit();
init(state, targetState);
}
private:
virtual
Note that my comment in the original question about the simplification provided by C++11's inherited constructors applies here too, but I have not updated the code in this question accordingly. That is left as an exercise to the reader.
The variant
I like the event/action syntax of the solution I proposed in my question. However, I really dislike the runtime penalty of creating/destroying states on the fly (I have made no measurement, but I dislike the principle).
I present hereafter a variant where the event/action syntax simplicity is pretty much kept (although slightly modified), but all states are created in advance. To keep my syntax, the states obviously need to be context-aware, which makes my solution anti-flyweight (one instance of every single concrete state per instance of a state machine). My feeling is that the cost in RAM is more affordable to the applications I have in mind than the runtime penalty. Another cost of this variant is a more complex declaration for states: declaration of state instances, need for a state to refer not only to the state machine but also to the nearest superstate.
Obviously, a straight application of the GoF's state pattern is an alternative for applications that need both flyweight and runtime efficiency (at the expense of event/action syntax simplicity).
Please comment on this.
genericstate.h
```
#ifndef GENERICSTATE_H
#define GENERICSTATE_H
template
class GenericState
{
public:
static void init(State *&state, State &initState) {
state = &initState;
initState.entry();
}
protected:
explicit GenericState(StateMachine &m, State *&state) :
m(m), s(m), state(state) {}
explicit GenericState(StateMachine &m, State *&state, SuperState &s) :
m(m), s(s), state(state) {}
void change(State &targetState) {
exit();
init(state, targetState);
}
private:
virtual
Solution
I'm not too familiar with inheritance, but here are some stylistic things I've noticed:
-
There is no need to use the
This may also be an indication that you have a little too much in your class declarations, as you are trying to organize everything. You may consider putting these inherited
-
machine.h:
Member functions that are not supposed to modify data members, such as
These function names are misleading as they don't perform any of these actions:
If you still need these functions, despite the fact that they only print some hard-coded text, then at least give them more accurate names to indicate that they're printing something and not actually performing an action.
-
machine.cpp:
This is not a very maintainable style:
In case you may need to add additional lines, use curly braces:
This should also be done with the other related function.
-
There is no need to use the
public, private, and protected keywords repeatedly in the same class declaration. It just makes it harder to read and adds nothing of value. You should just use the needed keywords once, and you can optionally leave out the private keyword since classes are private by default (this may also be less-readable overall, but it's up to you).This may also be an indication that you have a little too much in your class declarations, as you are trying to organize everything. You may consider putting these inherited
structs into separate files and then forward-declaring them in the needed classes after including them in the headers.-
machine.h:
Member functions that are not supposed to modify data members, such as
print(), should be const to prevent any accidental modifications. It probably also doesn't need to be static.void print(const std::string &str) const { std::cout << str << std::endl; }These function names are misleading as they don't perform any of these actions:
void entry() { print("entering High"); }
void bringDown() { print("already Low"); }
void exit() { print("leaving Low"); }
void entry() { print("entering Low"); }
void bringDown() { print("already Low"); }
void exit() { print("leaving Low"); }If you still need these functions, despite the fact that they only print some hard-coded text, then at least give them more accurate names to indicate that they're printing something and not actually performing an action.
-
machine.cpp:
This is not a very maintainable style:
if (color == BLUE) change(s.blue);
else ColorState::paint(color);In case you may need to add additional lines, use curly braces:
if (color == BLUE)
{
change(s.blue);
}
else
{
ColorState::paint(color);
}This should also be done with the other related function.
Code Snippets
void print(const std::string &str) const { std::cout << str << std::endl; }void entry() { print("entering High"); }
void bringDown() { print("already Low"); }
void exit() { print("leaving Low"); }
void entry() { print("entering Low"); }
void bringDown() { print("already Low"); }
void exit() { print("leaving Low"); }if (color == BLUE) change(s.blue);
else ColorState::paint(color);if (color == BLUE)
{
change(s.blue);
}
else
{
ColorState::paint(color);
}Context
StackExchange Code Review Q#41690, answer score: 3
Revisions (0)
No revisions yet.