Skip to content

Commit 59974ad

Browse files
committed
First Commit
1 parent 64a5ce2 commit 59974ad

13 files changed

Lines changed: 522 additions & 2 deletions

File tree

.github/workflows/coverage.yml

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
name: Coverage Report
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
9+
jobs:
10+
build-and-test:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout repo
15+
uses: actions/checkout@v3
16+
17+
- name: Install coverage tools
18+
run: sudo apt-get update && sudo apt-get install -y lcov
19+
20+
- name: Configure CMake with coverage
21+
run: cmake -S . -B build -DENABLE_COVERAGE=ON
22+
23+
- name: Build
24+
run: cmake --build build --config Debug -- -j
25+
26+
- name: Run tests
27+
run: ctest --output-on-failure --test-dir build
28+
29+
- name: Collect coverage
30+
run: |
31+
lcov --capture --directory build --output-file coverage.info
32+
lcov --remove coverage.info '/usr/*' '*/test/*' '*/tests/*' --output-file coverage.info
33+
lcov --list coverage.info
34+
35+
- name: Upload to Codecov
36+
uses: codecov/codecov-action@v4
37+
with:
38+
files: coverage.info
39+
fail_ci_if_error: true
40+
verbose: true

.github/workflows/test.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches: [main, master]
6+
pull_request:
7+
branches: [main, master]
8+
9+
jobs:
10+
build-and-test:
11+
runs-on: ${{ matrix.os }}
12+
13+
strategy:
14+
matrix:
15+
os: [ubuntu-latest, macos-latest, windows-latest]
16+
17+
steps:
18+
- name: Checkout repo
19+
uses: actions/checkout@v3
20+
21+
- name: Setup CMake
22+
uses: jwlawson/actions-setup-cmake@v1
23+
24+
- name: Configure CMake
25+
run: cmake -S . -B build
26+
27+
- name: Build
28+
run: cmake --build build --config Release -- -j
29+
30+
- name: Run tests
31+
run: ctest --output-on-failure --test-dir build

.gitignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,13 @@
3939

4040
# debug information files
4141
*.dwo
42+
43+
# CMake files
44+
CMakeCache.txt
45+
CMakeFiles/
46+
CMakeLists.txt.user
47+
CTestTestfile.cmake
48+
cmake_install.cmake
49+
Makefile
50+
build/
51+

.vscode/settings.json

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
{
2+
"files.associations": {
3+
"array": "cpp",
4+
"atomic": "cpp",
5+
"bit": "cpp",
6+
"cctype": "cpp",
7+
"charconv": "cpp",
8+
"clocale": "cpp",
9+
"cmath": "cpp",
10+
"compare": "cpp",
11+
"concepts": "cpp",
12+
"cstdarg": "cpp",
13+
"cstddef": "cpp",
14+
"cstdint": "cpp",
15+
"cstdio": "cpp",
16+
"cstdlib": "cpp",
17+
"ctime": "cpp",
18+
"cwchar": "cpp",
19+
"cwctype": "cpp",
20+
"deque": "cpp",
21+
"string": "cpp",
22+
"unordered_map": "cpp",
23+
"vector": "cpp",
24+
"exception": "cpp",
25+
"algorithm": "cpp",
26+
"functional": "cpp",
27+
"iterator": "cpp",
28+
"memory": "cpp",
29+
"memory_resource": "cpp",
30+
"numeric": "cpp",
31+
"optional": "cpp",
32+
"random": "cpp",
33+
"string_view": "cpp",
34+
"system_error": "cpp",
35+
"tuple": "cpp",
36+
"type_traits": "cpp",
37+
"utility": "cpp",
38+
"format": "cpp",
39+
"initializer_list": "cpp",
40+
"iosfwd": "cpp",
41+
"limits": "cpp",
42+
"new": "cpp",
43+
"numbers": "cpp",
44+
"ostream": "cpp",
45+
"span": "cpp",
46+
"stdexcept": "cpp",
47+
"streambuf": "cpp",
48+
"text_encoding": "cpp",
49+
"typeinfo": "cpp",
50+
"variant": "cpp"
51+
}
52+
}

CMakeLists.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
cmake_minimum_required(VERSION 3.16)
2+
project(CXXStateTree VERSION 0.1.0 LANGUAGES CXX)
3+
4+
set(CMAKE_CXX_STANDARD 20)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
7+
add_library(CXXStateTree INTERFACE)
8+
target_include_directories(CXXStateTree INTERFACE include)
9+
10+
add_executable(basic examples/basic.cpp)
11+
target_link_libraries(basic PRIVATE CXXStateTree)
12+
13+
# GoogleTest setup
14+
include(FetchContent)
15+
FetchContent_Declare(
16+
googletest
17+
URL https://github.com/google/googletest/archive/refs/heads/main.zip
18+
)
19+
# For Windows: Prevent overriding the parent project's compiler/linker settings
20+
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
21+
FetchContent_MakeAvailable(googletest)
22+
23+
24+
enable_testing()
25+
add_executable(state_tree_test tests/state_tree_test.cpp)
26+
target_link_libraries(state_tree_test PRIVATE CXXStateTree gtest_main)
27+
28+
include(GoogleTest)
29+
gtest_discover_tests(state_tree_test)
30+
31+
option(ENABLE_COVERAGE "Enable coverage reporting" OFF)
32+
33+
if(ENABLE_COVERAGE)
34+
message(STATUS "Building with coverage flags")
35+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 --coverage")
36+
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
37+
endif()

README.md

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,102 @@
1-
# CXXStateTree
2-
A C++ Hierarchical State Tree Library
1+
<p align="center">
2+
<img src="assets/logo.png" alt="CXXStateTree Logo" width="200"/>
3+
</p>
4+
5+
<h1 align="center">CXXStateTree</h1>
6+
<p align="center"><i>Modern Hierarchical State Machine for C++20</i></p>
7+
8+
## 🚀 Features
9+
10+
* 🔧 Fluent builder API with lambda-based DSL
11+
* ⚡ Fast runtime performance with zero heap allocation
12+
* 🛡️ Optional guards and actions for transitions
13+
* 🔄 Event-based state transitions
14+
* 🧪 Google Test integration
15+
* 📈 Code coverage with Codecov
16+
* 🌳 Designed for extensibility: nested states, DOT export coming soon
17+
18+
---
19+
20+
## 🛠️ Quick Example
21+
22+
```cpp
23+
#include <iostream>
24+
#include "CXXStateTree/StateTree.hpp"
25+
26+
using namespace CXXStateTree;
27+
28+
int main() {
29+
auto machine = Builder()
30+
.initial("Idle")
31+
.state("Idle", [](State& s) {
32+
s.on("Start", "Running", nullptr, []() {
33+
std::cout << "Idle -> Running" << std::endl;
34+
});
35+
})
36+
.state("Running", [](State& s) {
37+
s.on("Stop", "Idle", nullptr, []() {
38+
std::cout << "Running -> Idle" << std::endl;
39+
});
40+
})
41+
.build();
42+
43+
machine.send("Start");
44+
machine.send("Stop");
45+
}
46+
```
47+
48+
---
49+
50+
## 🧪 Running Tests
51+
52+
```bash
53+
cmake -S . -B build -DENABLE_COVERAGE=ON
54+
cmake --build build
55+
cd build && ctest
56+
```
57+
58+
---
59+
60+
## 📦 Dependencies
61+
62+
* C++20 compiler (GCC >= 10, Clang >= 11, MSVC >= 2019)
63+
* [GoogleTest](https://github.com/google/googletest) (auto-downloaded via CMake)
64+
65+
---
66+
67+
## 📈 Code Coverage
68+
69+
[![codecov](https://codecov.io/gh/your-org/your-repo/branch/main/graph/badge.svg)](https://codecov.io/gh/your-org/your-repo)
70+
71+
---
72+
73+
## 📂 Directory Structure
74+
75+
```
76+
CXXStateTree/
77+
├── include/CXXStateTree/ # Public header-only API
78+
├── examples/ # Usage examples
79+
├── tests/ # Google Test suite
80+
├── CMakeLists.txt # Project build
81+
└── .github/workflows/ci.yml # GitHub Actions CI
82+
```
83+
84+
---
85+
86+
## 📋 License
87+
88+
MPL2.0 License — see [LICENSE](LICENSE) for details.
89+
90+
---
91+
92+
## 🌟 Coming Soon
93+
94+
* Nested (hierarchical) state support
95+
* DOT/Graphviz state diagram export
96+
* Transitions with context/parameters
97+
98+
---
99+
100+
## 👋 Contributions Welcome
101+
102+
Issues, feature suggestions, and PRs are welcome!

assets/logo.png

1.33 MB
Loading

examples/basic.cpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// File: examples/basic.cpp
2+
#include <iostream>
3+
#include "CXXStateTree/StateTree.hpp"
4+
#include "CXXStateTree/Builder.hpp"
5+
6+
using namespace CXXStateTree;
7+
8+
int main()
9+
{
10+
auto machine = Builder()
11+
.initial("Idle")
12+
.state("Idle", [](State &s)
13+
{ s.on("Start", "Running", nullptr, []()
14+
{ std::cout << "Transition: Idle -> Running" << std::endl; }); })
15+
.state("Running", [](State &s)
16+
{
17+
s.on("Pause", "Paused", nullptr, []() {
18+
std::cout << "Transition: Running -> Paused" << std::endl;
19+
});
20+
s.on("Stop", "Idle", nullptr, []() {
21+
std::cout << "Transition: Running -> Idle" << std::endl;
22+
}); })
23+
.state("Paused", [](State &s)
24+
{ s.on("Resume", "Running", nullptr, []()
25+
{ std::cout << "Transition: Paused -> Running" << std::endl; }); })
26+
.build();
27+
28+
std::cout << "Initial state: " << machine.current_state() << std::endl;
29+
machine.send("Start");
30+
std::cout << "Current state: " << machine.current_state() << std::endl;
31+
machine.send("Pause");
32+
std::cout << "Current state: " << machine.current_state() << std::endl;
33+
machine.send("Resume");
34+
std::cout << "Current state: " << machine.current_state() << std::endl;
35+
machine.send("Stop");
36+
std::cout << "Current state: " << machine.current_state() << std::endl;
37+
38+
return 0;
39+
}

include/CXXStateTree/Builder.hpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#pragma once
2+
3+
#include "StateTree.hpp"
4+
5+
namespace CXXStateTree
6+
{
7+
8+
class Builder
9+
{
10+
public:
11+
Builder &initial(const std::string &name)
12+
{
13+
initial_state_ = name;
14+
return *this;
15+
}
16+
17+
Builder &state(const std::string &name, std::function<void(State &)> config)
18+
{
19+
State s(name);
20+
config(s);
21+
states_.insert({name, std::move(s)});
22+
return *this;
23+
}
24+
25+
StateTree build()
26+
{
27+
if (initial_state_.empty())
28+
{
29+
throw std::runtime_error("Initial state not set");
30+
}
31+
return StateTree(states_, initial_state_);
32+
}
33+
34+
private:
35+
std::unordered_map<std::string, State> states_;
36+
std::string initial_state_;
37+
};
38+
} // namespace CXXStateTree

include/CXXStateTree/State.hpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#pragma once
2+
3+
#include "Transition.hpp"
4+
5+
namespace CXXStateTree
6+
{
7+
8+
class State
9+
{
10+
public:
11+
explicit State(std::string name) : name_(std::move(name)) {}
12+
13+
void on(const std::string &event, const std::string &target,
14+
Guard guard = nullptr, Action action = nullptr)
15+
{
16+
transitions_[event] = Transition{target, guard, action};
17+
}
18+
19+
std::optional<Transition> get_transition(const std::string &event) const
20+
{
21+
auto it = transitions_.find(event);
22+
if (it != transitions_.end())
23+
return it->second;
24+
return std::nullopt;
25+
}
26+
27+
const std::string &name() const { return name_; }
28+
29+
private:
30+
std::string name_;
31+
std::unordered_map<std::string, Transition> transitions_;
32+
};
33+
} // namespace CXXStateTree

0 commit comments

Comments
 (0)