For various reasons, I wrote this. For one, I don't like to wrap every single function in a separate class, which is too verbose.
It's a state machine based on GoTW 57 function pointer wrapping and CRTP to make things readable, at least in client code parts.
template <class Derived>
struct StateMachine {
typedef Derived Self;
typedef StateMachine<Derived> Base;
//http://www.gotw.ca/gotw/057.htm
struct Func;
typedef Func (Derived::*UpdateFuncPtr)();
struct Func {
UpdateFuncPtr updateFunc;
Func(UpdateFuncPtr ptr) : updateFunc(ptr) {}
};
//actual statemachine .. can be as complex as needed
Func currentState;
StateMachine( Func init) : currentState(init) {}
void update() {
UpdateFuncPtr p = currentState.updateFunc;
Self * pThis = (Self *)this;
Func newPtr = (pThis->*p)();
if(newPtr.updateFunc)
currentState = newPtr;
}
};
The client looks like this:
struct DemoSM : StateMachine<DemoSM> {
int check; //just checking that *this is sane
DemoSM() : Base( &Self::update1 ) {
check = 42;
}
private:
Func update1() {
log("update1 called, check %d\n\r",check);
return &Self::update2;
}
Func update2() {
log("update called\n\r");
return NULL;
}
};
And just checking:
DemoSM machine;
machine.update();
machine.update();
Any critique or suggestions for improvements or gotchas welcome. The core principle is that the state machine functions themselves code, procedurally (not declaratively in a table), how the transitions get triggered - by returning the function that needs to be called next.
This can be made marginally more interesting with separate "update" and "state transition" functions maybe with different signatures.
I could not get GoTW suggested operator()
to work for taking pointer to members. C++ is insistent on &Class::member;
syntax there.