I often have the need to implement an object which is capable of switching its behaviour in response to a user command. For example, this could be the case of a class representig device connected to a PC and controlled by the user via a GUI. More generally, the device has to live on its own, with its own operation scheduling. Since I'd like to "extract" this behaviour from the specific device class in order to enhance code re-use, here I propose a templated finite state machine class using Qt. I also reported an example usage in class A. What do you ( more experienced programmers than me :) think about that? Is it the "correct" way to design such a class? Are there performance issues ?
template < class Base,
typename T,
class ThreadPolicy>
class FSM
{
public:
typedef bool (Base::*my_func)();
struct SState {
SState(){}
SState(const T& id_arg,
const T& next_arg,
const T& error_arg,
const QList<T>& branches_arg,
const my_func& op_arg) :
id(id_arg),
next(next_arg),
error(error_arg),
branches(branches_arg),
op(op_arg)
{}
T id; // state ID
T next; // next state
T error; // in case of error
QList<T> branches; // allowed state switching from current
my_func op; // operation associated with current state
};
typedef QMap<T ,SState> SMap;
bool switchState(const T& ns){
return _checkAllowed(ns);
}
bool addState(const T& id, const SState& s){
return _register(id, s);
}
protected:
void _loop(Base* ptr){
if ((ptr->*m_states[m_state].op)()) {
ThreadPolicy::Lock();
if(m_externalSwitch){
m_externalSwitch = false;
ThreadPolicy::Unlock();
return;
}
m_state = m_states[m_state].next;
ThreadPolicy::Unlock();
} else {
ThreadPolicy::Lock();
if(m_externalSwitch){
m_externalSwitch = false;
ThreadPolicy::Unlock();
return;
}
m_state = m_states[m_state].error;
ThreadPolicy::Unlock();
}
}
bool _checkAllowed(const T& cmd){
if (!m_states[m_state].branches.contains(cmd)) { return false;}
ThreadPolicy::Lock();
m_state = cmd;
m_externalSwitch = true;
ThreadPolicy::Unlock();
return true;
}
bool _register(const SState& s){
if(m_states.find(s.id) != m_states.end()) { return false; } // state with same ID already exist
m_states[s.id] = s; // add the new state to the map
return true;
}
SMap m_states; // map states to Baseclass methods
T m_state; // holds my current state
bool m_externalSwitch; // check if user request a state switch
};
class A :
public QObject,
public FSM< A, QString, MultiThreaded >
{
Q_OBJECT
A(){
// SState startState; myState.branches << "start" << "stop";
_register(SState("start",
"start",
"stop",QStringList(("start","stop")),
&A::_doStart));
_register(SState("stop",
"stop",
"stop",QStringList(("stop","start")),
&A::_doStop));
}
private slots:
void run(){
for(;;){
_loop(this);
QCoreApplication::processEvents();
}
}
private:
bool _doStart(){ return true;}
bool _doStop(){ return true;}
};
A finite state machine may be implemented through software or hardware to simplify a complex problem. Within an FSM, all states in consideration exist in a finite list and the abstract machine can only take on one of those states at a time. This approach allows each input and output scenario to be studied and tested.
A system where particular inputs cause particular changes in state can be represented using finite state machines. This example describes the various states of a turnstile. Inserting a coin into a turnstile will unlock it, and after the turnstile has been pushed, it locks again.
A finite state machine is a mathematical abstraction used to design algorithms. In simpler terms, a state machine will read a series of inputs. When it reads an input, it will switch to a different state. Each state specifies which state to switch to, for a given input.
A. What do you ( more experienced programmers than me :) think about that? Is it the "correct" way to design such a class? Are there performance issues ?
OK! I had a rough look at your design, and it doesn't really feel good to me for a general purpose FSM framework. That's too narrow to be usable in a widened context. Some points of critique:
In general I'd recommend to follow the GoF State Pattern for FSM implementation. For very simple state diagrams a switch(event)
case <event>: changeState(newState)
might be enough. But to render events as method entries of the FSM, and delegate these to the current state class instance, makes the whole construct much more flexible. Think about optional parameters coming along with a particular event, and you'll need to extend your state machine design for these.
In general your approach to use a CRTP for your state machine is a good idea, but for what you demonstrated, simple dynamic polymorphism (using virtual member functions) would work well also.
About performance issues, don't think you will hit ones with your current environment, but that completely depends where and in which context you want to deploy.
I want to recommend you having a look at my State Machine Class Template framework STTCL, which provides various C++ template based aspects of UML 2.0 compliant state machines, following the already mentioned GoF State Pattern.
if it is still relevant, I have implemented a finite state machine in C++ which uses the Object OP, it is fairly simple to use and if you look at the main.cpp there is an example.
The code is here and it is compiled as a library now.
Finite State Machine
Let me know if it is what you want!
Cheers,
Andrea
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With