State Diagram

State Diagram is an easy to use C++ library that supports specifying and executing hierarchical finite-state machines.

Download Version 1.3.2 (including all documentation), or just the PDF manual.


State Diagram Advantages

Ease of use means that State Diagram is carefully tuned to make it readily applicable in production environments.

Free and Open Source, Generous Apache License

State machines are often deployed in settings that are safety- and/or security-critical. Tools and libraries used in such settings should be open-source, a requirement that is met by State Diagram.

State Diagram is generously licensed according to the Apache 2.0 license. The aim with this license is to let users #include State Diagram freely in proprietary code bases, regardless of whether linking is static or dynamic.

The Best of Two Worlds: Intuitive and Powerful Drawing Analogy in the Form of a C++ Library

State Diagram lets users specify state machines directly in C++ yet still in a way that's akin to drawing them on a GUI, without any of the hassle and disadvantages that GUI's usually incur. Consider the following hello-world example:

Hello world state machine

The diagram shows a drawing of a state machine that prints "Hello world!" inside the terminal. This state machine can be specified using State Diagram like so:

#include "state_diagram/state_diagram.h"
    
using namespace state_diagram;
    
...
    
// The top state of the state machine
FSM_TOP(helloWorld);                 
    
// Void signal used to trigger the state machine
FSM_SIGNAL(void, sayHelloWorld, helloWorld);  
    
// An initial state directly beneath the top state
FSM_INIT(helloWorld);                
    
// An ordinary state also directly beneath the top state
FSM_STATE(aboutToSayHello, helloWorld); 
    
// A final state directly beneath the top state
FSM_FINAL(helloWorld);                
    
// A (triggerless) "auto" transition
// from the initial state to aboutToSayHello
FSM_AUTO(helloWorld_INIT, aboutToSayHello);
    
// A (triggered) "step" transition from aboutToSayHello to helloWorld_FINAL. The transition
// carries an action parameterized by a lambda that achieves the desired side effect.
FSM_STEP(aboutToSayHello, helloWorld_FINAL, Trigger(sayHelloWorld), Action([]{printf("Hello world!\n");}));
    
// Intializing the state machine
helloWorld.init();

// Letting the state machine perform its initial step
helloWorld.step();

// Letting the state machine handle the signal that lets it print "Hello world!" 
helloWorld.step(sayHello);

The drawing analogy should have become obvious. Every macro invocation corresponds to a graphical state machine component, just without any actual graphics!

Compare that to the hassle of probably paying for a dedicated graphical state machine tool, learning how to use the GUI, actually drawing the state machine on the GUI, generating code, and importing that code into your other code.

Neither do you have to learn any template metaprogramming or any GoF-like state machine patterns that only go so far in being able to express what users often would like to design in practice. The drawing analogy is simple yet powerful.

Leveraging the Full Power of C++

Dedicated state machine tools derive their justification from the idea of supporting state machine design in real life; real life applications tend to make state machines big, complicated, and messy. To help coping with these complexities, state machine tools introduce various enhancements to their core design functionality, for example, ways of parameterizing state machines to make them more flexible and reusable.

The problem stems from the fact that state machine tools are really reinventing the wheel by doing all of these things, and rather imperfectly at that. No dedicated tool of this kind will ever come close to matching the expressive power and flexibility that C++ provides - object-oriented and increasingly functional language features, templates, language preprocessing, the entire universe of C++ libraries and C++-supporting tools. Because State Diagram code is C++ code, State Diagram allows developers to leverage each and everything the C++ ecosystem has to offer directly in producing state machine designs in practical settings.

To exemplify this thesis, assume the hello-world example was to be made generic by being able to parameterize it with what is to be printed. This requirement could easily be solved by putting the state machine into a function, like so:

void runHelloWorld(char const * const msg)
{
  FSM_TOP(helloWorld);
  FSM_SIGNAL(void, sayHelloWorld, helloWorld);
  FSM_INIT(helloWorld);                
  FSM_STATE(aboutToSayHello, helloWorld); 
  FSM_FINAL(helloWorld);                
  FSM_AUTO(helloWorld_INIT, aboutToSayHello);
  FSM_STEP(aboutToSayHello, helloWorld_FINAL, Trigger(sayHelloWorld), Action([&]{printf("%s\n", msg);}));
  helloWorld.init();
  helloWorld.step();
  helloWorld.step(sayHelloWorld);
}

The possibilities offered by C++ are endless. They could probably never be matched by any dedicated state machine tool.

Then, State Diagram adopts a very pure approach in the sense that computations on data and computations yielding side effects are not part of its C++ API. State Diagram delegates any of that to C++ as its host language via simple usage of lambdas - see the examples of a step action above. State Diagram, thus, does not have to reinvent the wheel in this respect either like it is mostly the case with any dedicated state machine tool. Yet again, it relies on the expressive power provided by C++ instead.

Last but not least and going beyond C++, think of any widely-used programming tool like the various IDEs, version control systems, unit test frameworks etc. These tools can be applied in working with State Diagram as directly as in any kind of C++ coding. They will never become as aware of any stand-alone state machine framework and its formats like they are aware of C++.

Lessening the C++ Learning Curve

Talking so much about C++, what if you really aren't too much into C++ but more into C instead? Look again at the examples above. Although they are written in C++, they turn out to be rather elementary. Much of them feels like C and what else there is consists of simple lambdas and some equally simple function calls using the .-notation on objects. All of that leads precisely to some of the beauty of State Diagram compared to other C++-based approaches: The drawing analogy relieves you from learning any complicated object-oriented or functional patterns before you can start using State Diagram. The popularity that lambdas have gained testifies to their versatility and ease of use; what remains to learn about C++ to be able to use State Diagram is not that much.

Expressive Features Analogous to UML Statecharts

State Diagram leans heavily on UML statecharts with regard to what kinds of transitions, transition decorations, and drawing patterns it supports. For this reason, developers find it easy to adopt State Diagram if they are used to graphical tools that, in their turn, are based on UML.

Likewise, the hierarchical execution model implemented in State Diagram is more or less the same as in UML statecharts. According to this model, a transition that has a sub-divided state as its source state can only fire if nothing inside the source state can currently fire.

Also, a macro-step consists of micro-steps, that is to say, individual transition firings. These firings continue as long as any transitions are enabled to fire according to where control flow resides, which trigger signals are activated, and which transition guards evaluate to true.

Signaling Model Inspired by Established Computer Science Research

Different state machine tools and libraries adopt different ways of treating signals. State Diagram draws inspiration from a concept that computer science research has identified as pragmatically sound and, at the same time, highly advantageous in verifying state machines. This model is termed "perfect synchrony." It holds, among other things, that a signal once activated during a macro-step stays activated over the entire remaining duration of that step. Crucially, perfect synchrony leads to an overall execution model that supports treating state machines compositionally. This property helps greatly if properties of state machines are to be verified using formal methods, or in any kind of state machine verification.

Concurrency Without Relying on Thread Libraries

State Diagram supports concurrent regions at all levels of the state hierarchy. The execution model realizes concurrency through execution in lockstep; it does not resort to using any thread libraries. A later stage will provide the option of mapping concurrent regions to actual threads.

Small Code Base

State Diagram with all its functionality exemplifies the power of modern C++ in that it consists of just a few thousand, heavily patterned lines of code. So there is only a small code base to contend with.


More on State Diagram

Is State Diagram the GoF Builder Pattern Applied to State Machines?

Yes and no.

Yes in the sense that the examples show how State Diagram can be used in a way that clearly resembles the builder pattern. There is nothing wrong about that because it already has all the advantages mentioned here.

No in the sense that the builder pattern is always dynamic. State Diagram allows developers to draw state machines in a way that, at the very least, looks rather declarative instead. This pattern of using State Diagram consists of declaring and defining state machine elements as directly initialized class data members. As an example of that, a reusable message printer could be drawn using a class definition as follows:

class MsgPrinter
{
private:
  CompoundState const & context;
  Signal<void> const & printMsg;
  string const msg;

public:
  MsgPrinter(CompoundState const & context, Signal<void> const & printMsg, string const & msg)
  :
    context{context}, printMsg{printMsg}, msg{msg}
  {}

private:
  FSM_INIT(context);
  FSM_STATE(aboutToPrintMsg, context);
  FSM_FINAL(context);
  FSM_AUTO(context_INIT, aboutToPrintMsg);
  FSM_STEP(aboutToPrintMsg, context_FINAL, Trigger(printMsg), Action([&]{printf("%s\n", msg.c_str());}));
};

What Does "Perfect Synchrony" Really Mean?

Mostly referring to the relevant literature, it can still be mentioned that perfect synchrony asserts that, on a certain abstract level, it should be possible to view signal activation as timeless. So, on that abstract level, it must be possible to view signals as either active or inactive over the entire duration of each and every macro-step. Signals still need to be activated by means of micro-steps, that is to say, individual transitions. Activations, however, are regarded as a matter of causality via transition triggering, not as a matter of them occurring at different points of time. Timeless-ness and causality are tied together by the fact that transition triggers are always positive, in the sense that a trigger lets a transition fire only if the underlying signal is active. There exist no "negative" triggers or guard predicates testing for a signal not being activated. Think about it: If triggers are always positive, then there can never be any situation where a sequence of micro-steps contains a trigger on a signal being not active (since this kind of thing simply does not exist) followed by this signal being activated. This situation would be precisely the one where the signal could not be considered active or inactive over the entire duration of the macro step.

What Roadmap is There for State Diagram?

Major milestones to be achieved in the future are C code generation, thread-based concurrency, formal verification, and after-the-fact graphical rendering.

Technicalities

State Diagram can be compiled with GCC 9.1 and higher, and with Clang 12. Option --std=c++2a is required unless C++20 is the default. Technically, it is possible to compile State Diagram with some earlier versions of GCC too, but only in the sense that these compilations produce binaries. Testing showed that such binaries contain compiler-induced bugs.


Contact

Please e-mail to user info at host domain state-diagram.com.