Authorship
ZFSM and this Website were created and are maintained by Michael Baldamus (http://linkedin.com/in/michael-baldamus) under the legal disclaimers shown below.
Source code
Download Version 0.1 of the ZFSM library at http://mbaldamus.com/zfsm/zfsm.hpp or http://codeberg.org/zfsm.
Overview
ZFSM is a header-only C++ library for hierarchical finite state machines (FSM*). Its main features are as follows:
- Inspired by the SyncChart approach to hierarchical finite state machines.
- Small footprint, currently less than 1500 lines of code.
- Adhering to C++14 as implemented in GCC and Clang.
- Six types of states are supported:
- Top states.
- Superstates.
- Composite states which consist of concurrent regions.
- Basic states.
- Initial and final states.
- Six types of arrows (transitions) are supported:
- React.
- Continue.
- (Immediate) Abort.
- (Immediate) Suspend.
- Besides user-specified payloads associated with any type of arrow, payloads can also be associated with hierarchical states and concurrent regions. There are four execution disciplines for such payloads:
- On state or region entry or exit.
- Prior or after any execution tick within the state or region.
- Strong emphasis on state modularity by forbidding arrows that cross state or region boundaries.
- A signaling mechanism that supplants boundary-crossing arrows while being compatible with modularity besides serving other purposes.
- State hierarchies can be expressed naturally in terms of object composition.
- Supporting modular reuse of hierarchical states and regions by expressing them as class instances.
- Incurring heap usage only when state machines are constructed, while no heap usage is incurred later on.
- Using callables, normally lambdas, as arrow guards and payloads, as well as using callables as action payloads.
- Mix-in architecture via which states and regions only ever instantiate the resources they need for the specific purpose at hand.
- Deterministic, i.e. scheduling-free execution of concurrent regions.
- Neither virtual inheritance nor RTTI used in implementing all this functionality.
- Configurable error handling.
- Free and open source, generous Apache 2.0 license.
(* The 'Z' in 'ZFSM' stands for 'ultimate' since 'Z' is the last letter in the English alphabet.)
Hello-world example
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 sayHello;
assert (!sayHello.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 he 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 state 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.
Library reference
Basics
- ZFSM provides a C++ programming framework for hierarchical finite state machines.
- ZFSM is a header-only library contained in a file zfsm.hpp.
- All use of the ZFSM library is governed by Apache 2.0 license.
- ZFSM adheres to C++14 as implemented in newer versions of GCC and Clang.
- All user-level elements of ZFSM live in name space zfsm.
- None of the user-level elements of ZFSM can be copied, moved, or assigned-to, that is to say, all of them can only be constructed in place either on the stack or the heap.
- ZFSM in and of itself does not induce any heap allocations during state machine execution. Excecuting user-supplied callables, that is to say, user-supplied arrow guards and payloads can still induce heap allocations. (Such heap allocations can, of course, not be controlled at the state machine level.)
- ZFSM is not thread-safe.
States and regions
Overview
- The following table gives an overview of how to construct states and regions.
| 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 |
☞ Context is always an internal class template parameter. Actual constructor arguments need be instances of one of the user-level context class templates Top<Capability...>, Super<Capability...>, Composite<Capability...>, and Region<Capability...>, or of user-defined subclasses thereof.
☞ Likewise, Init is always an internal class template parameter. Actual constructor arguments need be instances of the user-level class template, Init<Capability...>. It's an error if an initial state is used as a constructor argument for a context within which it is not placed by virtue of its own construction.
☞ Constructing a top state must be accompanied by calling member function fuse() on it exactly once.
☞ ZFSM does not perfom any lifetime management of state machine components.
Capabilities
- Capabilities are enum constants used configure states and regions with the resources they need to carry signals, arrows, and/or actions. The following table shows which capabilities exist and which signal, arrow, and action types they enable.
| 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 |
|
| ImmediateAbort |
| ImmediateSuspend |
| OnEntry | OnExit | DoFront | DoBack |
☞ Capabilities, zero or more depending on what needs be enabled, are provided as stare or region class template parameters.
☞ Arrows are always source-attached to the state that carries them - So that the target never has to be provided with the same capability unless it serves as the source of other arrows of the same type.
- The following table shows which capabilities are supported by type.
| 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 | ✓ | | | | ✓ | ✓ | ✓ |
☞ If a state or region is provided with a capability it does not support, then this provision is ignored, that is to say, it does not trigger any error condition.
State hierarchy
- The following table shows what state hierarchy relationships are possible in ZFSM. Some relationships are mandatory, which means that a context must have an element of the given type — top states, superstates, and regions must have exactly one designated initial state, and composite states must have at least one region. Otherwise, a context may have one or more elements of the given type. If there is a 0, then the context element in question cannot appear in the given context.
| 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 |
☞ A composite state with just one region is equivalent to a superstate whose contents are identical to that region's. The reason why this pattern is not flagged an error is that it may sometimes be useful in debugging state machines.
Signals
- A signal is a condition that can be set during an execution tick — It will automatically reset at the end of this tick. The following table shows the signal constructor plus what member functions exist on signals.
| 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 |
The following table shows the signal constructor plus what member functions exist on void, that is to say, non-value carrying signals.
| 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 |
☞ The context becomes the scope of the signal, that is to say, the signal can be used in all contexts that lie anywhere in the hierarchy underneath.
☞ Using the signal outside of its scope leads to undefined behavior but is currently not explicitly flagged as an error condition. It's up to users to ensure that signals are never used out-of-scope.
Arrows
- The following table shows the arrow types provided by ZFSM.
| 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 an execution sequence within that state has finished/td> |
| 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 |
- The following table shows the main arrow constructors, where Arrow can be one of the types shown in the previous table.
| Arrow<T>(Source &source, std::function<Carry<T>()> guard, Target &target, std::function<void(T)> payload) |
| Arrow<>(Source &source, std::function<bool()> guard, Target &target, std::function<void()> payload) |
|
☞ If an arrow type is parameterized with a non-void type of T, then the arrow guard must yield a carry if the guard's outcome is that the arrow fires. In that case the guard ought to return this result by means of a return statement of the form return {true, expression}, where the expression part forms the carry value. The carry value is automatically forwarded to the payload, which must be a callable taking a T as its argument. If the arrow does not fire, then the guard ought to return false and nothing else. In that case no constructor on T will be called to construct the carry, whence T need not be default constructible.
☞ If the template argument to the arrow type is empty or, equivalently, if is void, then the guard must always yield a bool while the payload must be parameter-less.
☞ If a lambda is used as a guard, and this lambda returns a carry, then it may be required to declare the carry type as the lambda's return type. Analogously, bool-returning guards may have to have bool declared as their return type.
☞ Both Source and Target must be state types. Cross-border arrows are not supported, that is to say, source and target must always lie within the same context. If that is not the case, then the way in which the state machine behaves is undefined.
- The following table shows arrow constructors providing various shortcuts for the main constructors from the previous table.
| 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 |
☞ If a signal appears as a guard, then that guard will fire if, and only if, that signal is raised at the time of the guard being evaluated.
- The following table shows which source/target combinations are supported by which arrow types.
| Target |
| Init | Final | Basic | Super | Composite |
| Source | Init | - |
|
|
|
|
| Final | - | - | - | - | - |
| Basic | - |
|
|
|
|
| Super | - | all | all | all | all |
| Composite | - | all | all | all | all |
☞ To summarize, an initial state can never be the target of any arrow, while a final state can never be the source of any arrow. A basic state can only be the source of React and Continue arrows. Only superstates and composite states can be the source or the target of any type of arrow.
State actions
- The following table shows the action types provided by ZFSM.
| 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 |
- The following table shows the main action constructors, where Action can be one of the types shown in the previous table.
| Action<T>(Context *context, std::function<Carry<T>()> guard, std::function<void(T)> payload) |
| Action<>(Context *context, std::function<bool()> guard, std::function<void()> payload) |
|
☞ Carry values within actions work completely analogous to carry values within arrows.
- The following table shows arrow constructors providing various shortcuts for the main constructors from the previous table — See above for analogous arrow constructors.
| 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) |
Execution discipline
- The execution discipline is straightforward and recursive. The following points describe it in detail.
- Basic terminology:
- A hierarchical state is a superstate or composite state.
- A preemptive arrow is an Abort or Suspend arrow.
- An immediately preemptive arrow is an ImmediateAbort or ImmediateSuspend arrow.
- An arrow guard opens if evaluating it yields true in the case of a bool-returning guard, or a carry with true in its first position in the case of a carry-returning guard, or if the guard consists of a signal and the signal is raised at the point of time of evaluating it as a guard.
- A top state, superstate, or concurrent region has terminated if its current state is a final state. A composite state has terminated if all of its concurrent regions have terminated.
- Suspending a state means suspending it in full depth, something which is often called a deep suspend. ZFSM has no direct support for suspending states only at their topmost level while resetting every level underneath it, which is what is often just called a suspend.
- The initial state of a state machine is the initial state of its top state.
- An execution tick is initiated by calling member function bool tick(...) on the state machine's top state. This function returns true if, having completed the execution tick, the state machine has not terminated, false otherwise. Arguments passed to tick are of the form signal.raise() or signal.raise(expression). (In this way, signals to be used as inputs for the execution tick can be raised together with the call to tick itself.)
- Executing a top state, superstate, or concurrent region means the following.
- If the current state is an initial state, a basic state, or a hierarchical state that has terminated, then the React arrows sourced at this state are considered in the order in which they have been constructed. Among these arrows, the first arrow whose guard opens is executed.
- If the current state is a superstate or composite state that has not yet terminated, then the following execution discipline applies:
- The immediately preemptive arrows sourced at this state are considered in the order in which they have been constructed. Among these arrows, the first arrow whose guard opens is executed.
- If no immediately preemptive arrow is executed, then the hierarchical state itself is executed. Executing a hierarchical state means recursive descent in the case of a superstate and recursive descent into each region in the case of a composite state. Regions are executed in the order in which they have been constructed.
- Once the hierarchical state is finished executing, and given it has not yet terminated, the preemptive arrows sourced at this state are considered in the order in which they have been constructed. Among these arrows, the first arrow whose guard opens is executed.
- Once the first React arrow has been executed, Continue arrows are executed until there are no more Continue arrows anymore whose guards open.
- Within any context, only the arrows sourced at that context's current state are eligible for execution. Executing an arrow means the following.
- If the arrow is a preemptive or immediately preemptive arrow, then the following applies:
- If the arrow is an Abort or ImmediateAbort arrow, then a deep reset is applied to its source state — So that the source returns to its initial state.
- If the arrow is a Suspend or ImmediateSuspend arrow, then the source state is suspended — So that the source continues in its state if and when it is re-entered.
- Unless the arrow is a Suspend or ImmediateSuspend arrow, the source state is exited and returns to its initial state.
- The arrow's payload is executed. It the payload expects a carry, then the carry is supplied from the arrow guard.
- The arrow's target state becomes the new current state.
- If the target state is a hierarchical state, then it is entered given it has not been suspended previously. In case it has been suspended, it continues from its suspended state. In either case, the following holds.
- If the target state is a superstate, then Continue arrows are executed within it until there are no more Continue arrows anymore whose guards open.
- If the target state is a composite state, then Continue arrows are executed within its regions until there are no more Continue arrows anymore whose guards open. These execution sequences take place in the order in which these regions have been constructed.
- State actions are executed as follows but always in the order in which they have been constructed.
- OnEntry and OnExit actions on a context are executed whenever the context is entered or exited, respectively. As an exception, OnEntry actions are not executed whenever a suspended state is re-entered.
- DoFront and DoBack actions on a context are executed before or after every execution sequence within that context, respectively. — They are not executed when entering the context.
- If a scope is exited during an execution tick, then signals raised in this scope are reset immediately, that is to say, not waiting for the end of the overall execution tick. (Handling signals in this way is related to what is known as the reincarnation problem, which is something that cannot be dwelled upon at this place.)
- Executing a state machine can be terminated at any time by calling member function exit() on its top state. Calling this function leads to a deep reset being performed on the state machine followed by performing all OnExit actions on the top state.
Error handling
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:
- One and the same instance of class Init used as the designated initial state in more than one context.
- Signals used out of scope.
What's new
- August 24, 2026: Version 0.1 released.
Versioning policy
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.
Contact
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 spammers lay their hands on the real address. 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 sending e-mail, you agree to the legal disclaimer shown below.
Legal disclaimers
Legal disclaimer regarding ZFSM source code
All use of ZFSM source code is governed by the Apache 2.0 license, which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
Legal disclaimer regarding this Website
Privacy
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 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 two weeks after receiving it. Replying to a message may usually take two 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 code generated by Doxygen has been removed manually.)
Warranty disclaimer
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.
Imprint
This Website is owned by Michael Baldamus, Lindgårdsvägen 28, 743 64 Björklinge, Sweden.
Copyright
Copyright © 2026 by the Website Owner, all rights reserved.