DEV Community

Discussion on: A Functional-Style State Machine in C++

Collapse
 
tmr232 profile image
Tamir Bahar • Edited

Hi David,

I would like to address your analysis, as well as add some of my insights.

Reason 1: Switch Statements & Compile-Time Guarantees

In the past, switch statements provided very weak compile time guarantees. It used to be impossible to ensure case exhaustion (as enum types were ints)
and you could always get accidental fallthroughs.

With moden C++ and recent compilers, this is changing. Enum classes allow compilers to check for case exhaustion (-Wswitch). Then can also ensure that all cases are handled explicitly (-Wswitch-enum). Additionally, C++17's [[fallthrough]] helps ensure that there are no accidental fallthroughs (-Wimplicit-fallthrough). See here.

In my opinion, those are fairly good guarantees. Additionally, the switch statement design makes the code easier to hold in your head and reason about. You can clearly see all the state transitions at the same time.

Reason 2: Context

The State pattern allows for the different states to hold their own contexts internally. It is not, however, a critical part of the design. You can either hold the context in each individual state, or hold a state-machine wide context and pass it around. This can be done either by the States holding a reference to the context as a private member, or by using stateless states and passing the context on each call.
In my design, the state-functions are stateless and the context is passed into them with every call. This decouples the context from the state functions, and allows for far easier testing. To test a state function, I simply pass it a context representing a given situation, and an event to handle. No need for any further set-up. Were the States holding internal context, it would have had to be set up before every test, and therefore would have had to be exposed in one way or another, complicating the design.

Conclusion

Given the switch-related compiler warnings, it seems to me that both options are reasonably sound. With that in mind, I think that the choice between the functional-style state machine and the State pattern is mainly a stylistic one. I much prefer this solution as it has less textual-overhead. That said, the State pattern is well known and well used - so it very well might solve some issues not-yet addressed here.