Question

I am working on a finite state machine library, with these public methods:

template <typename STATE_T>
void add_state();              // allocate STATE_T on heap - STATE_T must derive from state

template <typename FROM_STATE_T, typename TO_STATE_T>
void link_state(std::function<bool()> cond, unsigned int priority);

// search for base state pointer that can be dynamically caste d to STATE_T* type, set this state as the beginning state
template <typename STATE_T>
void begin_state();

void execute();

//STATE_T must be a user defined state class that inherits from the base state class.

I chose a run-time implementation over compile time, because the interface would be more complicated when using variadic template parameters for states. However, there are constraints that I want to enforce to make sure the programmer does not introduce bugs when implementing their state machine.

Here are the constraints I want to enforce:

  1. Cannot add duplicate states (ie. adding two of the same derived type)
  2. Cannot link states that do not exist (such as a state type not in the fsm list of states)
  3. States must have an entry point/be reachable
  4. A begin and end state must exist
  5. execute cannot be run multiple times concurrently

Assertion aborts the program and clearly adheres to these constraints, but is it the correct choice?

It is possible for 1,2,3 to be violated and the fsm still be in a valid state (simply do nothing) but I dont like the idea of implicitly handling these, because it introduces a false sense of security by hiding programmer faults.

If I throw exceptions for 1,2,3 and the programmer catches them, then fsm could still be in a valid state, allowing an ill-formed fsm to run.

5 is something that should not be done. should I handle this, or leave it as UB?

What would be the most appropriate mechanism to use for these constraints?

Was it helpful?

Solution

This is a rather typical problem of handling errors. The answer usually depends on costs. What is the cost of the problem going unnoticed? What is the cost of the program getting aborted by exception?

In most cases, the cost of a program crash is not so big. The user will just restart it. In that case, you should go for unhandled exception. This way, you'll notice the bugs fast, fix them, and end up with a better program.

There's a hybrid approach: in DEBUG build, handle the error with an "Assertion failed" messagebox (usually this is done with ASSERT() macro), but in Release build, handle the error silently. This, however, lets the problem to go unnoticed on client's computers, often triggering other errors, which are hard to find.

Finally, about your worries that programmer can handle exception: it isn't something you should think about. You indicated a fatal error, if programmer ignores it, it's his fault.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top