ZFSM and this Website were created and are maintained by Michael Baldamus (https://linkedin.com/in/michael-baldamus) under the legal disclaimers shown below.
Code patterns and graphical tools for state machines are widely used and often support states that are contained in each other or, equivalently, state hierarchies. Most often, such approaches permit arrows (also known as transitions) across state boundaries. One of the simplest examples of that would be two states a and b and an arrow drawn from a to a substate c of b. Now, does not everybody (largely) believe in Dijkstra's famous dictum, "Go To Statement Considered Harmful"? ZFSM has its starting point in the observation that arrows across state boundaries are nothing but the equivalent of goto's in the state machine realm. In the example, the arrow from a to c breaks encapsulation present in b because it depends on knowing that b is a superstate and that c is one of its substates.
ZFSM provides a framework for hierarchical state machines. While adopting an object-oriented approach where hierarchies can be expressed in terms of object composition, it also provides means of supplanting arrows going across state boundaries. In synergy, these two main features provide excellent support for keeping state machines internally modular and their components reusable. Many practical applications of hierarchical state machines consist of deep hierarchies and hundreds if not thousands of states, together with the necessity of supporting different variants thereof. By way of focusing on modularity and reusablity, ZFSM should be particularly useful in such settings.
ZFSM state machines are not (variants of) UML state machines or, to use more precise terminology, UML Statecharts. I found that the objectives pursued by ZFSM are more compatible with the SyncChart approach to state machines, so it is SyncCharts that ZFSM adapts for its purposes.
Download Version 0.1.4 of the ZFSM library at https://mbaldamus.com/zfsm/zfsm.hpp.
ZFSM is a C++ library for hierarchical finite state machines. (The 'Z' in 'ZFSM' stands for 'ultimate' since 'Z' is the last letter in the English alphabet.) Its main features are as follows:
As a state machine library, ZFSM takes the approach of designing state machines directly at the programming language level. Compare that to employing some dedicated framework, which would usually come in the form of some kind of GUI environment with its own file formats. GUI approaches may appear more user-friendly on the face of it, but the classical advantage of working with a library is being able to combine state machine patterns with the given language's and its ecosystem's full power in terms of complementary programming patterns and tool support. The point is that the age of AI brings another advantage, which is that dedicated frameworks, be they solely state-machine oriented or even more general like UML, will always have far less training data accessible to AI than any general programming language ecosystem. — With such ecosystems, AI constantly ingests and improves itself on gargantuan and continually growing amounts of all kinds of code accessible in all kinds of repositories. Hence the programming language approach to designing state machines, as opposed to utilizing any more restricted framework, should always find better overall support by AI.
Here is a most basic hello-world example of using ZFSM.
#include <cassert>
#include <iostream>
#include "zfsm.hpp"
class SayHello : public zfsm::Top<>
{
zfsm::Init<zfsm::REACT> init{this};
zfsm::Final final{this};
zfsm::React<> init_2_final{init, final, []{ std::cout << "ZFSM is saying hello!" << std::endl; }};
public:
SayHello() : zfsm::Top<>(init) { fuse(); }
};
int main()
{
SayHello example;
assert (!example.tick());
return 0;
}
This example features a state machine type, SayHello, whose instances have two substates, init and final, and one react arrow, init_2_final, from init to final. This arrow has no guard and a payload, which writes 'ZFSM is saying hello!' to stdout. The arrow is triggered by calling a member function, tick(), on an instance, sayHello, of SayHello. The state machine terminates by executing the arrow, whence tick() returns false.
Please note that all user-level elements of ZFSM live in a name space zfsm, a property we will not mention in the remainder of this section anymore.
Going back to the example, SayHello inherits from a template class Top, here instantiated with an empty parameter list because SayHello does not require any non-default capabilities. — Sub-states and arrows as class members is a default capability of top states and, thus, does not have to be specified in the parameter list. States init and final are members of SayHello. Both have to be contextualized with the top state by putting this as an initialization parameter. They are instances of classes Init and Final, respectively, where Init has to be provided with a capability of REACT because a react arrow is attached to init. This arrow is another member of SayHello. It is initialized with its source state, init, its target state, final, and a payload action in the form of a lambda. Constructing SayHello requires initializing Top with init to tell it which substate is the initial one. Moreover, it requires calling a post-construction member function fuse(). This call has to be issued once per top state.
Please note how this example shows that ZFSM supports expressing relationships between superstates and substates in terms of object composition. It needs be pointed out, too, that internal arrows, i.e. arrows between direct substates, do not appear as member functions, as would be the case according to more classical state machine patterns. All arrows are data and internal arrows can be instance members just like substates.
The basic a, b, and c example has a as the initial state of its top state, b as a superstate within the top state, and c as a nested state within b. Instead of drawing a direct arrow from a to c, a React arrow is drawn from a to b together with drawing a Continue arrow from b's initial state to c. These two arrows in combination entail that the transition from a to b with c as its internal state is made within one tick while, at the same time, avoiding any boundary-crossing arrow or, in other words, affording b a clean, unbroken interface. There are other state machine libraries and tools that support this pattern or similar ones, even if boundary-crossing arrows are supported in principle, but we will explain what the real point is when it comes to next example. For the moment, please note how the basic a, b, and c example already shows how ZFSM supports mapping state hierarchies one-to-one to class syntax, here in the form of B appearing as a nested class inside of ABC.
#include <cassert>
#include <iostream>
#include "zfsm.hpp"
using namespace zfsm;
class ABC : public Top<>
{
Init<REACT> a{this};
class B : public Super<CONTINUE>
{
Init<CONTINUE> init{this};
Basic<REACT> c{this};
Final final{this};
Continue<> init_2_c{init, c, []{ std::cout << "Basic A-B-C test." << std::endl; }};
React<> c_2_final{c, final};
public:
B(ABC *abc) : Super<CONTINUE>(abc, init) {}
}
b{this};
Final final{this};
React<> a_2_b{a, b};
Continue<> b_2_final{b, final};
public:
ABC() : Top<>(a) { fuse(); }
};
int main()
{
ABC example;
assert (example.tick());
assert (!example.tick());
return 0;
}
Some of the most common patterns involving boundary-crossing arrows use fork transitions, that is to say, transitions that fork into different concurrent regions of a composite state while, at the same time, bypassing any initial states in these regions to target follow-on states directly. This kind of pattern is complex and, what is more, it breaks all encapsulation present in the composite state. — Fork transitions totally depend on open access to and knowledge about the composite state's sub-hierarchy, at least up to and including its own regions.
ZFSM proposes foregoing any such patterns, in this specific case supplanting them by combining React and guarded Continue transitions, where signals are used to guide control flow across state boundaries while, at the same time, affording the composite state complete encapsulation in the sense that its environment actually has no way of "enforcing" it to be composite. In other words, the encapsulation is such that the composite state can be supplanted by any other state or state hierarchy of the same external behavior as long as the signaling interface stays the same.
To focus soleley on the point just made, the more advanced a, b, and c example is very simple in almost all other respects. It does, however, exhibit a reuse pattern by introducing two free-standing region classes, R and S, with substates c1 and c2 or d1 and d2, respectively. These classes are instantiated inside of b, which is now composite. "Forking" into b is a consequence of immediate follow-on states on entering b being determined dynamically. Another framework might use two different fork transitions, one targeting c1 and d1, the other one targeting c2 and d2. By contrast, the modular approach proposed here introduces a value-carrying signal sig used in guarding Continue arrows targeting the four substates. The net effect, as advertised, is that b presents an interface just consisting of its ability to receive sig.
#include <cassert>
#include <iostream>
#include "zfsm.hpp"
using namespace zfsm;
enum class Token {T1, T2};
class R : public Region<>
{
Signal<Token> &sig;
Init<CONTINUE> init{this};
Basic<REACT> c1{this};
Basic<REACT> c2{this};
Final final{this};
Continue<> init_2_c1{init, [&]{ return sig() && (sig.getValue() == Token::T1); }, c1};
React<> c1_2_final{c1, final, []{ std::cout << "Got Token::T1" << std::endl; }};
Continue<> init_2_c2{init, [&]{ return sig() && (sig.getValue() == Token::T2); }, c2};
React<> c2_2_final{c2, final, []{ std::cout << "Got Token::T2" << std::endl; }};
public:
template<class Ctxt>
R(Ctxt *ctxt, Signal<Token> &s) : Region<>(ctxt, init), sig(s) {}
};
class S : public Region<>
{
Signal<Token> &sig;
Init<CONTINUE> init{this};
Basic<REACT> d1{this};
Basic<REACT> d2{this};
Final final{this};
Continue<> init_2_d1{init, [&]{ return sig() && (sig.getValue() == Token::T1); }, d1};
React<> d1_2_final{d1, final, []{ std::cout << "Received Token::T1" << std::endl; }};
Continue<> init_2_cd{init, [&]{ return sig() && (sig.getValue() == Token::T2); }, d2};
React<> d2_2_final{d2, final, []{ std::cout << "Received Token::T2" << std::endl; }};
public:
template<class Ctxt>
S(Ctxt *ctxt, Signal<Token> &s) : Region<>(ctxt, init), sig(s) {}
};
class ABC : public Top<SIGNAL>
{
public:
Signal<Token> sig{this};
private:
Init<REACT> a{this};
class B : public Composite<CONTINUE>
{
R r;
S s;
public:
B(ABC *ctxt, Signal<Token> &sig) : Composite<CONTINUE>(ctxt), r(this, sig), s(this, sig) {}
}
b{this, sig};
Final final{this};
React<> init_2_b{a, b};
Continue<> b_2_final{b, final};
public:
ABC() : Top<SIGNAL>(a) { fuse(); }
};
int main()
{
ABC example;
ASSERT(example.tick(example.sig.raise(Token::T1)));
ASSERT(!example.tick());
example.exit();
ASSERT(example.tick(example.sig.raise(Token::T2)));
ASSERT(!example.tick());
return 0;
}
To dwell on modularity a little further, the following example shows how b can be supplanted by a non-composite state of exactly the same behaviour without changing anything around it. Boundary-crossing fork arrows would make replacing b in such a direct way impossible.
#include <cassert>
#include <iostream>
#include "zfsm.hpp"
using namespace zfsm;
enum class Token {T1, T2};
class ABC : public Top<SIGNAL>
{
public:
Signal<Token> sig{this};
private:
Init<REACT> a{this};
class B : public Super<CONTINUE>
{
Signal<Token> &sig;
Token tok;
Init<CONTINUE> init{this};
Basic<REACT> c{this};
Final final{this};
Continue<Token> init_2_c{init, sig, c, [&](Token t){ tok = t; }};
React<> c_2_final{c, final, [&]{
switch (tok) {
case Token::T1:
std::cout << "Got Token::T1" << std::endl << "Received Token::T1" << std::endl;
break;
case Token::T2:
std::cout << "Got Token::T2" << std::endl << "Received Token::T2" << std::endl;
break;
default:
assert (false);
}
}};
public:
B(ABC *ctxt, Signal<Token> &s) : Super<CONTINUE>(ctxt, init), sig(s) {}
}
b{this, sig};
Final final{this};
React<> init_2_b{a, b};
Continue<> b_2_final{b, final};
public:
ABC() : Top<SIGNAL>(a) { fuse(); }
};
int main()
{
ABC example;
ASSERT(example.tick(example.sig.raise(Token::T1)));
ASSERT(!example.tick());
example.exit();
ASSERT(example.tick(example.sig.raise(Token::T2)));
ASSERT(!example.tick());
return 0;
}
| Top<Capability...>(Init& init) | A top state with a designated initial state within the context directly underneath |
| Init<Capability...>(Context *context) | An initial state within an enclosing context |
| Final(Context *context) | A final state within an enclosing context |
| Basic<Capability...>(Context *context) | A basic state within an enclosing context |
| Super<Capability...>(Context *context, Init& init) | A superstate within an enclosing context, together with a designated initial state within the context directly underneath |
| Composite<Capability...>(Context *context) | A composite state within an enclosing context |
| Region<Capability...>(Context *context, Init& init) | A region within an enclosing context, which must a composite state, together with a designated initial state within the context directly underneath |
| Signal-related | Arrow-related | Action-related | |||||||||||
| Capability: | SIGNAL | REACT | CONTINUE | PREEMPT | IMMEDIATE_PREEMPT | ON_ENTRY | ON_EXIT | DO_FRONT | DO_BACK | ||||
| Types(s) enabled: | Signal | React | Connect |
|
| OnEntry | OnExit | DoFront | DoBack | ||||
| State types | Region type | |||||||
| Top | Init | Final | Basic | Super | Composite | Region | ||
| Capabilities | SIGNAL | ✓ | ✓ | ✓ | ✓ | |||
| REACT | ✓ | ✓ | ✓ | ✓ | ||||
| CONTINUE | ✓ | ✓ | ✓ | ✓ | ||||
| PREEMPT | ✓ | ✓ | ||||||
| IMMEDIATE_PREEMPT | ✓ | ✓ | ||||||
| ON_ENTRY | ✓ | ✓ | ✓ | ✓ | ||||
| ON_EXIT | ✓ | ✓ | ✓ | ✓ | ||||
| DO_FRONT | ✓ | ✓ | ✓ | ✓ | ||||
| DO_BACK | ✓ | ✓ | ✓ | ✓ | ||||
| Context | |||||
| Top | Super | Composite | Region | ||
| Context element | Signal | ≥ 0 | ≥ 0 | ≥ 0 | ≥ 0 |
| Init | 1 | 1 | 0 | 1 | |
| Final | ≥ 0 | ≥ 0 | 0 | ≥ 0 | |
| Basic | ≥ 0 | ≥ 0 | 0 | ≥ 0 | |
| Super | ≥ 0 | ≥ 0 | 0 | ≥ 0 | |
| Composite | ≥ 0 | ≥ 0 | 0 | ≥ 0 | |
| Region | 0 | 0 | ≥ 1 | 0 | |
| Signal<T>(Context *context) | Constructing a value-carrying signal within an enclosing context. |
| raise(T const &value) | Raising the signal, together with copying the value to be carried |
| raise(T &&value) | Raising the signal, together with moving the value to be carried |
| bool operator() | Query operator returning true, if and only if, the signal has been raised |
| T const& getValue() | Obtaining a const reference to the signal value |
| T&& takeValue() | Obtaining an r-value reference to the signal value for it to become movable |
| Signal<>(Context *context) | Constructing a void signal within an enclosing context. |
| raise() | Raising the signal, together with copying the value to be carried |
| bool operator() | Query operator returning true, if and only if, the signal has been raised |
| React | An arrow that can be executed at the beginning of a tick |
| Continue | An arrow that can be executed in succession to executing one of the other arrow types |
| Abort / Suspend | An arrow that can be executed to abort or suspend a superstate or composite state, but only after a recursive execution sequence within that state has finished |
| ImmediateAbort / ImmediateSuspend | An arrow that can be executed to abort or suspend a superstate or composite state, and without starting to execute the current tick within that state |
|
| Arrow<>(Source &source, Target &target) | An arrow without any guard and without any payload |
| Arrow<>(Source &source, std::function<bool()> guard, Target &target) | An arrow with a guard but without any payload |
| Arrow<>(Source &source, Target &target, std::function<void()> payload) | An arrow without a guard but with a payload |
| Arrow<>(Source &source, Signal<T> &signal, Target &target) | An arrow with a signal as its guard, and without any payload |
| Arrow<T>(Source &source, Signal<T> &signal, Target &target, std::function<void(T)> payload) | An arrow with a value-carrying signal as its guard, and with a matching payload |
| Arrow<>(Source &source, Signal<T> &signal, Target &target, std::function<void()> payload) | An arrow with a value-carrying signal as its guard, and with a payload that does not require any value argument |
| Target | ||||||||||||||
| Init | Final | Basic | Super | Composite | ||||||||||
| Source | Init | - |
|
|
|
| ||||||||
| Final | - | - | - | - | - | |||||||||
| Basic | - |
|
|
|
| |||||||||
| Super | - | all | all | all | all | |||||||||
| Composite | - | all | all | all | all | |||||||||
| OnEntry | An action executed when entering the context it belongs to |
| OnExit | An action executed when exiting the context it belongs to |
| DoFront | An action executed prior to executing a complete tick sequence within the context it belongs to |
| DoBack | An action executed after executing a complete tick sequence within the context it belongs to |
|
| Action<>(Context *context, std::function<void()> payload) |
| Action<T>(Context *context, Signal<T> &signal, std::function<void(T)> payload) |
| Action<>(Context *context, Signal<T> &signal, std::function<void()> payload) |
| void fuse() | Fusing the state machine right after constructing it |
| bool tick(...) | Triggering an execution tick performed by the state machine according to the recursive displine described below |
| void exit() | Performing a complete reset on the state machine |
☞ Handling signals in the way just described is related to what is known as the reincarnation problem, which is something that cannot be dwelled upon at this place. Suffice it to say that the reincarnation problem occurs in connection with a fundamental concepts called perfect synchrony, execution intervals or macrosteps vs. microsteps in executing synchronous programs. These concepts have their origin in emulating the workings of electric circuitry in software. While ZFSM is inspired by synchronous programming in general and SyncCharts in particular, it foregoes implementing these concepts in any bona fidae way. The only parallel that could be made is execution ticks being a kind of lightweight version of synchronous macrosteps. The tradeoff is that ZFSM practically does not have to introduce any extra overhead to deal with the relation between signals being raised or not and the flow of causality during macrosteps or execution ticks, to the extent that the macrostep/execution tick analogy holds. These matters are what normally cause restrictions on how to use signals and/or computational overhead in implementing synchronous macrosteps in software. One of the net effects of ZFSM's non-electric approach is the reincarnation problem not being an issue.
☞ In a similar vein, ZFSM does not require arrow guards to be free of any observable side effects. ZFSM guarantees that guards are evaluated in order whenever arrows are considered for execution according to the recursive execution discipline, and not any more often than that. Here, the outcome is, while it could be considered desirable to keep arrow guards free of any observable side effects, it does not lead to any undefined behaviour if users introduce them as they see fit.
By default, most errors are flagged by throwing instances of class Exception, which itself derives from std::exception. Defining symbol ZFSM_ERRORS_ASSERT replaces exceptions by assert's becoming false; Defining symbol ZFSM_ERRORS_ASSUME_NONE makes the library code assume that no errors occur. These two symbols must not be defined at the same time. Then, two types of errors are currently not flagged at all:
The following example summarizes how signals can directly occur as guards. These patterns can occur in all types of arrows and context actions.
#include <cassert>
#include <iostream>
#include "zfsm.hpp"
using namespace zfsm;
class SignalsAsArrowGuards : public Top<SIGNAL, ON_ENTRY>
{
public:
Signal<> a{this};
Signal<int> b{this};
private:
Init<REACT> init{this};
Basic<CONTINUE> s{this};
Basic<CONTINUE> t{this};
Final final{this};
React<> init_2_s{init, a, s, []{ std::cout << "Non-value carrying signal a used as a guard." << std::endl; }};
Continue<> s_2_t{s, b, t, []{ std::cout << "Value-carrying signal b used as a guard while not retrieving its value." << std::endl; }};
Continue<int> t_2_final{t, b, final, [](int v){ std::cout << "Value-carrying signal b used as a guard, retrieving value of " << v << '.' << std::endl; }};
public:
SignalsAsArrowGuards() : Top<SIGNAL, ON_ENTRY>(init) { fuse(); }
};
int main()
{
SignalsAsArrowGuards example;
assert (!example.tick(example.a.raise(), signalsAsArrowGuards.b.raise(42)));
return 0;
}
The following example shows all possibilities of placing context actions.
#include <cassert>
#include <iostream>
#include "zfsm.hpp"
using namespace zfsm;
class ContextActions : public Top<ON_ENTRY, DO_FRONT, DO_BACK, ON_EXIT>
{
OnEntry<> onEntry{this, []{ std::cout << "On entry at top" << std::endl; }};
DoFront<> doFront{this, []{ std::cout << "Do front at top" << std::endl; }};
DoBack<> doBack{this, []{ std::cout << "Do back at top" << std::endl; }};
OnExit<> onExit{this, []{ std::cout << "On exit at top" << std::endl; }};
Init<REACT> init{this};
class S : public Super<CONTINUE, ON_ENTRY, DO_FRONT, DO_BACK, ON_EXIT>
{
OnEntry<> onEntry{this, []{ std::cout << "On entry at s" << std::endl; }};
DoFront<> doFront{this, []{ std::cout << "Do front at s" << std::endl; }};
DoBack<> doBack{this, []{ std::cout << "Do back at s" << std::endl; }};
OnExit<> onExit{this, []{ std::cout << "On exit at s" << std::endl; }};
Init<CONTINUE> init{this};
class C : public Composite<CONTINUE, ON_ENTRY, DO_FRONT, DO_BACK, ON_EXIT>
{
OnEntry<> onEntry{this, []{ std::cout << "On entry at c" << std::endl; }};
DoFront<> doFront{this, []{ std::cout << "Do front at c" << std::endl; }};
DoBack<> doBack{this, []{ std::cout << "Do back at c" << std::endl; }};
OnExit<> onExit{this, []{ std::cout << "On exit at c" << std::endl; }};
class R : public Region<ON_ENTRY, DO_FRONT, DO_BACK, ON_EXIT>
{
OnEntry<> onEntry{this, []{ std::cout << "On entry at r" << std::endl; }};
DoFront<> doFront{this, []{ std::cout << "Do front at r" << std::endl; }};
DoBack<> doBack{this, []{ std::cout << "Do back at r" << std::endl; }};
OnExit<> onExit{this, []{ std::cout << "On exit at r" << std::endl; }};
Init<CONTINUE> init{this};
Basic<REACT> mid1{this};
Basic<REACT> mid2{this};
Final final{this};
Continue<> init_2_mid1{init, mid1, []{ std::cout << "init_2_mid1" << std::endl; }};
React<> mid1_2_mid2{mid1, mid2, []{ std::cout << "mid1_2_mid2" << std::endl; }};
React<> mid2_2_final{mid2, final, []{ std::cout << "mid2_2_final" << std::endl; }};
public:
R(C *ctxt) : Region<ON_ENTRY, DO_FRONT, DO_BACK, ON_EXIT>(ctxt, init) {}
}
r{this};
public:
C(S *ctxt) : Composite<CONTINUE, ON_ENTRY, DO_FRONT, DO_BACK, ON_EXIT>(ctxt) {}
}
c{this};
Final final{this};
Continue<> init_2_c{init, c, []{ std::cout << "init_2_c" << std::endl; }};
Continue<> c_2_final{c, final, []{ std::cout << "c_2_final" << std::endl; }};
public:
S(ContextActions *ctxt) : Super<CONTINUE, ON_ENTRY, DO_FRONT, DO_BACK, ON_EXIT>(ctxt, init) {}
}
s{this};
Final final{this};
React<> init_2_c{init, s, []{ std::cout << "init_2_s" << std::endl; }};
Continue<> c_2_final{s, final, []{ std::cout << "s_2_final" << std::endl; }};
public:
ContextActions() : Top<ON_ENTRY, DO_FRONT, DO_BACK, ON_EXIT>(init) { fuse(); }
};
int main()
{
ContextActions example;
assert (example.tick());
assert (example.tick());
assert (!example.tick());
example.exit();
return 0;
}
The following example shows that values in signals and carries need not be default-constructible.
#include <cassert>
#include <iostream>
#include <ctime>
#include <cstdlib>
#include "zfsm.hpp"
using namespace zfsm;
struct WrapInt
{
int const wrapped;
WrapInt() = delete;
WrapInt(int k) : wrapped(k) {}
};
class NoDefaultConstructibleValues : public Top<SIGNAL>
{
public:
Signal<WrapInt> sig{this};
private:
Init<REACT> init{this};
Final final{this};
React<WrapInt> init_2_final{
init,
[&]()->Carry<WrapInt>{
if (sig()) {
return {true, sig.getValue()};
} else {
return false;
}},
final,
[](WrapInt wrapper){ std::cout << "Retrieving random value of " << wrapper.wrapped << std::endl; }};
public:
NoDefaultConstructibleValues() : Top<SIGNAL>(init) { fuse(); }
};
main()
{
NoDefaultConstructibleValues example;
std::srand(std::time({}));
assert (!example.tick(example.sig.raise(std::rand())));
return 0;
}
The following example shows a general pattern of reusing a state. Class Reusable provides a superstate type, which is instantiated with different external capabilities. The example contains two instances of S, one with an external capability of REACT, the other one with an external capability of CONTINUE.
#include <cassert>
#include <iostream>
#include "zfsm.hpp"
using namespace zfsm;
template <Capability... capability>
class Reusable : public Super<ON_ENTRY, ON_EXIT, capability...>
{
public:
template<typename Ctxt>
Reusable(Ctxt ctxt) : Super<ON_ENTRY, ON_EXIT, capability...>(ctxt, init) {}
private:
OnEntry<> onEntry{this, []{ std::cout << "At entry of S" << std::endl; }};
OnExit<> onExit{this, []{ std::cout << "At exit of S" << std::endl; }};
Init<CONTINUE> init{this};
Final final{this};
Continue<> init_2_final{init, final};
};
class StateReuse1 : public Top<>
{
Init<REACT> init{this};
Reusable<REACT> s{this};
Final final{this};
React<> init_2_s{init, s};
React<> s_2_final{s, final};
public:
StateReuse1() : Top<>(init) { fuse(); }
};
class StateReuse2 : public Top<>
{
Init<REACT> init{this};
Reusable<CONTINUE> s{this};
Final final{this};
React<> init_2_s{init, s};
Continue<> s_2_final{s, final};
public:
StateReuse2() : Top<>(init) { fuse(); }
};
main()
{
StateReuse1 example1;
assert (example1.tick());
assert (!example1.tick());
StateReuse2 example2;
assert (!example2.tick());
return 0;
}
The following, most basic example shows another general pattern of reusing a state, in this state by inheriting from it. What it means is that states cannot only be reused in a black-box fashion but can also be enriched, as it were, by using inheritance to add components to them.
#include <cassert>
#include <iostream>
#include "zfsm.hpp"
using namespace zfsm;
template <Capability... capability>
class Extensible : public Super<capability...>
{
public:
template<typename Ctxt, typename Init>
Extensible(Ctxt ctxt, Init &init) : Super<capability...>(ctxt, init) {}
Final final{this};
};
class StateExtension : public Top<>
{
Init<REACT> init{this};
class S : public Extensible<CONTINUE>
{
Init<CONTINUE> init{this};
Continue<> init_2_final{init, final};
public:
S(StateExtension *ctxt) : Extensible<CONTINUE>(ctxt, init) {}
}
s{this};
Final final{this};
React<> init_2_s{init, s};
Continue<> s_2_final{s, final};
public:
StateExtension() : Top<>(init) { fuse(); }
};
int main
{
StateExtension example;
ASSERT(!example.tick());
return 0;
}
The version number will be advanced whenever there is any update to the library souce code. Mere documentation updates will not be reflected in the version number advancing.
Please send e-mail to get into touch. To help avoiding the use of 'cookies' on this Website while still providing spam protection, you are kindly asked to solve a puzzle to obtain the e-mail address. The puzzle is simply this. Consider the mock e-mail address, 'zfsm(dot)amlryar889(at)mbaldamus(dot)com'. To obtain the real e-mail address, substitute each character in between the '.' to the left of the '@' and the '@' itself — here represented by '(dot)' or '(a)', respectivey. Each character is to be substituted by the second-next character from the English alphabet with a wrap-around of 'y' being replaced by 'a', and 'z' being replaced by 'b'; Each digit is to be substituted by this digit plus two modulo 10.
The variable part will change in the event that the actual address gets flooded with spam. So, if a user finds an older address not working, then the current address can always be retrieved by solving the current puzzle as it appears at that point of time on this Web page.
By using ZFSM or sending e-mail, you agree to the legal disclaimers shown below.
All use of ZFSM library is governed by the Apache 2.0 license, which can be obtained at https://www.apache.org/licenses/LICENSE-2.0.
Neither any "cookies" nor any third-party analytics are used on this Website*. Likewise, it is neither required nor even possible to open any personal account on this Website. This Website does not collect any personal data by any other method either. Like with all other Websites, it is unavoidable that accessing it leads to anonymous, server-side traffic statistics being generated, a fact not mentioned in many privacy policies. These statistics are never used to try and identify any individual user by working back from the data available.
The only partial exception to all of that is users being able to send e-mail messages to the address that is presented under the heading "Contact." E-mails received are kept on the servers of the Internet provider (henceforth just "the Provider") employed to host this Website for the sole purpose of being able to satisfy any inquiries made — given that reacting is deemed warranted in the first place. E-mail messages are never stored elsewhere than on the servers deployed by the Provider. Users have no reason to doubt that the Provider deploys industry-standard procedures, equipment, and architectural provisions to keep their data secure. The only transfer that ever happens takes place transiently to view messages online on client systems run by the Owner of this Website (henceforth just "the Owner") but without ever storing any messages on these systems. Usually, any message received is read within three weeks after receiving it. Replying to a message may usually take three more weeks. In case a message is not reacted to, then it is deleted immediately after coming to this conclusion; In case a message is reacted to, then it is kept only for as long as is necessary to satisfy the inquiry made.
The Owner never shares any user data with any third parties unless required by applicable law.
Relevant authorities will be notified immediately whenever the Owner suspects that any leakage of user data might have occurred.
(* Just in case anyone wonders, cookie functionality normally induced by Doxygen has been disabled manually.)
While the Owner endeavours to keep this Website up to date and correct, the information offered herein is provided strictly on an “as is” and “as available” basis. Partaking in this information is at Users’ own risk. To the maximum extent permitted by applicable law, the Owner expressly disclaims all conditions, representations, and warranties — whether express, implied, statutory or otherwise, including, but not limited to, any implied warranty of merchantability, fitness for a particular purpose, or non-infringement of third-party rights. No advice or information, whether oral or written, obtained by User from Owner or through this Website will create any warranty not expressly stated herein. Any reliance you place on such information is therefore strictly at Users' own risk. In no event will the Owner be liable for any loss or damage including without limitation, indirect or consequential loss or damage, or any loss or damage whatsoever arising from loss of data or profits arising out of, or in connection with, partaking in the information provided herein.
Without limiting the foregoing, the Owner, its subsidiaries, affiliates, licensors, officers, directors, agents, co-branders, partners, suppliers, and employees do not warrant that this Website's contents are accurate, reliable, or correct; that they will meet Users’ requirements; that this Website will be available at any particular time or location, uninterrupted or secure; that any defects or errors will be corrected; or that this Website is free of viruses or other harmful components. Any content downloaded or otherwise obtained through the use of this Website is downloaded at Users' own risk and users shall be solely responsible for any damage to Users’ computer system or mobile device or loss of data that results from such download or Users’ use of this Website.
Every effort is made to keep this Website up and running smoothly. However, the Owner takes no responsibility for, and will not be liable for, this Website being unavailable at any time and any length of time for any reason.
The Owner has no obligation whatsoever to provide any technical support to users. Any technical support on the Owner's part is provided under the terms of the Warranty Disclaimer and entirely voluntarily. It can be terminated at any time without any prior notification and for any reason.
This Website is owned by Michael Baldamus, Lindgårdsvägen 28, 743 64 Björklinge, Sweden.
Copyright © 2026 by the Website Owner, all rights reserved.