Building Better State Machines in Modern C++: CXXStateTree
State machines are the backbone of countless software systems - from game AI and UI flows to embedded devices and network protocols. Yet, implementing them in C++ often feels like we're stuck in the past: endless switch statements, tangled conditionals, and boilerplate that makes your eyes glaze over.
What if there was a better way?
🚀 Meet CXXStateTree
CXXStateTree is a modern, header-only C++20 library that brings elegance and performance to state machine development. It's designed for developers who want clean, maintainable code without sacrificing the performance C++ is known for.
💡 Why Another State Machine Library?
Fair question! Here's what makes CXXStateTree different:
⚡ Zero Heap Allocation
Every allocation counts in performance-critical applications. CXXStateTree is designed with a zero-allocation philosophy - everything stays on the stack where possible.
🔧 Fluent Builder API
No more fighting with complex constructors or configuration objects. Build your state machine the way you'd draw it on a whiteboard:
#include <iostream>
#include "CXXStateTree/StateTree.hpp"
using namespace CXXStateTree;
int main() {
auto machine = StateTree::Builder()
.initial("Idle")
.state("Idle", [](State& s) {
s.on("Start", "Running", nullptr, []() {
std::cout << "🚀 Starting engine..." << std::endl;
});
})
.state("Running", [](State& s) {
s.on("Stop", "Idle", nullptr, []() {
std::cout << "🛑 Stopping engine..." << std::endl;
});
})
.build();
machine.send("Start"); // Output: 🚀 Starting engine...
machine.send("Stop"); // Output: 🛑 Stopping engine...
}
Clean. Readable. Self-documenting.
🛡️ Guards and Actions
Real-world state machines need conditional logic. CXXStateTree lets you attach guards (conditions) and actions to transitions:
auto doorMachine = StateTree::Builder()
.initial("Closed")
.state("Closed", [](State& s) {
s.on("Open", "Open",
[]() {
// Guard: only open if unlocked
return !isDoorLocked();
},
[]() {
// Action: perform the opening
std::cout << "🚪 Opening door..." << std::endl;
}
);
})
.state("Open", [](State& s) {
s.on("Close", "Closed", nullptr, []() {
std::cout << "🚪 Closing door..." << std::endl;
});
})
.build();
🌳 Hierarchical States (Available!)
One of the most powerful features - nested states for modeling complex behaviors:
Game Character
├── Alive
│ ├── Idle
│ ├── Walking
│ └── Running
└── Dead
This allows you to handle events at different levels of specificity, reducing code duplication and improving maintainability.
🎯 Real-World Use Cases
CXXStateTree shines in:
- 🎮 Game Development: Character AI, game flow, animation controllers
- 📱 UI Development: Form wizards, navigation flows, modal states
- 🤖 Embedded Systems: Protocol handlers, device state management
- 🌐 Network Programming: Connection state machines, protocol implementations
- 🦾 Robotics: Behavior trees, task scheduling
📦 Getting Started
Requirements
- C++20 compiler (GCC ≥ 10, Clang ≥ 11, MSVC ≥ 2019)
- CMake (for building)
Installation
Option 1: Single Header (Recommended)
git clone https://github.com/ZigRazor/CXXStateTree.git
cd CXXStateTree
cmake -S . -B build -DENABLE_SINGLE_HEADER=ON
cmake --build build
The single header file will be in single_include/CXXStateTree.hpp - just drop it into your project!
Option 2: Shared Library
cmake -S . -B build
cmake --build build
Quick Example: Traffic Light
Let's build a simple traffic light system:
#include <iostream>
#include <thread>
#include <chrono>
#include "CXXStateTree/StateTree.hpp"
using namespace CXXStateTree;
using namespace std::chrono_literals;
int main() {
auto trafficLight = StateTree::Builder()
.initial("Red")
.state("Red", [](State& s) {
s.on("Timer", "Green", nullptr, []() {
std::cout << "🔴 -> 🟢 RED to GREEN" << std::endl;
});
})
.state("Green", [](State& s) {
s.on("Timer", "Yellow", nullptr, []() {
std::cout << "🟢 -> 🟡 GREEN to YELLOW" << std::endl;
});
})
.state("Yellow", [](State& s) {
s.on("Timer", "Red", nullptr, []() {
std::cout << "🟡 -> 🔴 YELLOW to RED" << std::endl;
});
})
.build();
// Simulate traffic light cycle
for (int i = 0; i < 6; i++) {
std::this_thread::sleep_for(2s);
trafficLight.send("Timer");
}
return 0;
}
🏗️ Project Quality
CXXStateTree isn't just elegant code - it's built with professional software engineering practices:
- ✅ Google Test Integration - Comprehensive unit test suite
- ✅ Codecov Integration - Track test coverage
- ✅ GitHub Actions CI/CD - Every commit is validated
- ✅ Modern CMake - Easy integration into existing projects
🗺️ Roadmap
The project has an exciting future ahead:
| Status | Version | Features |
|---|---|---|
| ✅ | v0.1.0 | Basic state machine with transitions |
| ✅ | v0.2.0 | Guards and actions |
| ✅ | v0.3.0 | Nested hierarchical states |
| ✅ | v0.4.0 | Graphviz export |
| 🚧 | v0.5.0 | Coroutine/async support |
| 📋 | v1.0.0 | Full test coverage, benchmarks, docs |
🤝 Contributing
CXXStateTree is open source under the MPL 2.0 license and welcomes contributions!
Ways to contribute:
- 🐛 Report bugs and issues
- 💡 Suggest new features
- 🔧 Submit pull requests
- 📚 Improve documentation
- ⚡ Share performance benchmarks
🎓 Learning Resources
Want to dive deeper? Check out:
- GitHub Repository
- Examples Directory
- Test Suite - Great for understanding usage patterns
💭 Final Thoughts
State machines don't have to be a pain in C++. With CXXStateTree, you get:
- Developer Productivity - Write less, express more
- Maintainability - Clear structure that scales
- Performance - Zero-overhead abstractions
- Modern C++ - Built for C++20 and beyond
- Active Development - Regular updates and improvements
Whether you're building a game, an embedded system, or a complex business application, CXXStateTree provides a solid foundation for managing state transitions cleanly and efficiently.
🚀 Try It Today
git clone https://github.com/ZigRazor/CXXStateTree.git
Give it a star ⭐ if you find it useful, and share your experience in the comments below!
What kind of state machines are you building? Drop a comment and let's discuss! 💬
Top comments (6)
Hi, I have taken a look and I have some questions:
Is there a way of ignoring events that are not handled in current state, instead of throwing?
What I mean: imagine a elevator state machine with external button. User calls GoUp, elevator state goes Idle->GoingUp, and in this state, user is still pushing GoUp button which should have no effect. I don't want it to throw, but ignore this event.
Is there a way of performing actions on entry/exit of the state, instead of particular transition?
Assume there are many connections to i.e. Idle state. StateA->Idle, StateB->Idle, StateC->Idle. Same with going Idle->StateA/B/C (three transitions). Instead of executing something on each of these transitions, I only want to write this code once, on Idle entry, and Idle exit.
Heap allocations - you wrote:
"where possible" is concerning - so does it mean there are always 0 dynamic allocations, or sometimes it allocates?
Hi Kate,
I answer you:
1) in this moment is possible to ignore event without throwing managing the event with null action. You can open an issue to add the functionality to automatically ignore events not managed.
2) In this moment is not provided this kind of mechanism, but will be developed soon
3) There is no explicit new in the code, but obviously the use of standard library container implicitly use heap-allocation.
Thank you for you feedback, if you have other question you can ask me.
Feel free to open issue for new functionalities or bug that you find.
Great,
1- I have opened the issue.
2- good! It will be useful.
3- well so it means the whole benefit of:
Zero Heap Allocationis not true and should not be raised. If embedded system is restricted to avoid heap allocations, it cannot use this state machine implementation.It would be cool to add like the compile time option DISABLE_HEAP that would ensure that heap is not used at all, but it would be huge change in code, as there are many stl containers used now... but it would be really beneficial for embedded systems
1) thank you so much;
2) You can open an issue for this new functionality
3) I agree with you, it could be misundestood. I think that you could open an issue with the request for this major change. And it will be developed ASAP.
Thank you in advance!
Looks interesting - I will play with it in my free time and try to use it in embedded sw.
Thank you so much for your insterest, give me feedback on it.