Or more generically, look into Hierarchical State Machines.
There's a few insights that you can apply to your state machines that will give you most of the power you'll ever need:
1. Rather than storing an enum in your state variable, store a function pointer to state-functions.
2. Rather than have the states check outward for data to operate on, pass in a generic event type. There will still be some amount of looking outward, perfection is the enemy of good (a simple enum that the state function switch()-es on is enough to experiment with the idea).
3. Have a single entry point that takes an event and dispatches it to the states. That way you can trigger an arbitrary number of events to the state machine for any one external event (like after a transition, sending in "state exited" and "state entered" events).
4. Hierarchy: Add a mechanism by which a state-function can ignore the event, and the dispatching code sends the event to the state's parent state instead.
I think the rest that I gained from reading about and working with complex state machines is about creating good APIs for dealing with the ideas above, and to design the state diagram on paper before implementing. I rarely use the full complexity of the "QHsm" described in the book I linked, but the concepts aren't strangers and I'll often start with a switch(_state){} and sprinkle in features as needed.
Or more generically, look into Hierarchical State Machines.
There's a few insights that you can apply to your state machines that will give you most of the power you'll ever need:
1. Rather than storing an enum in your state variable, store a function pointer to state-functions.
2. Rather than have the states check outward for data to operate on, pass in a generic event type. There will still be some amount of looking outward, perfection is the enemy of good (a simple enum that the state function switch()-es on is enough to experiment with the idea).
3. Have a single entry point that takes an event and dispatches it to the states. That way you can trigger an arbitrary number of events to the state machine for any one external event (like after a transition, sending in "state exited" and "state entered" events).
4. Hierarchy: Add a mechanism by which a state-function can ignore the event, and the dispatching code sends the event to the state's parent state instead.
I think the rest that I gained from reading about and working with complex state machines is about creating good APIs for dealing with the ideas above, and to design the state diagram on paper before implementing. I rarely use the full complexity of the "QHsm" described in the book I linked, but the concepts aren't strangers and I'll often start with a switch(_state){} and sprinkle in features as needed.