├── CNAME ├── projects ├── code-lite │ ├── test │ │ └── tags │ └── hfsm2.workspace ├── premake │ ├── temp-14.vcxproj.filters │ ├── temp-15.vcxproj.filters │ ├── temp-16.vcxproj.filters │ ├── temp-17.vcxproj.filters │ ├── temp-clang.vcxproj.filters │ ├── snippets-14.vcxproj.filters │ ├── snippets-15.vcxproj.filters │ ├── snippets-16.vcxproj.filters │ ├── snippets-17.vcxproj.filters │ ├── snippets-clang.vcxproj.filters │ └── tools.pyproj └── visual-studio │ ├── shared-vs-14.props │ ├── vs-flags.txt │ ├── temp-14.vcxproj.filters │ ├── temp-15.vcxproj.filters │ ├── temp-16.vcxproj.filters │ ├── temp-17.vcxproj.filters │ ├── temp-clang.vcxproj.filters │ ├── calculator-14.vcxproj.filters │ ├── calculator-15.vcxproj.filters │ ├── calculator-16.vcxproj.filters │ ├── calculator-17.vcxproj.filters │ ├── calculator-clang.vcxproj.filters │ ├── basic_audio_player-14.vcxproj.filters │ ├── basic_audio_player-15.vcxproj.filters │ ├── basic_audio_player-16.vcxproj.filters │ ├── basic_audio_player-17.vcxproj.filters │ ├── basic_audio_player-clang.vcxproj.filters │ ├── basic_traffic_light-14.vcxproj.filters │ ├── basic_traffic_light-15.vcxproj.filters │ ├── basic_traffic_light-16.vcxproj.filters │ ├── basic_traffic_light-17.vcxproj.filters │ ├── basic_traffic_light-clang.vcxproj.filters │ ├── debug_logger_interface-14.vcxproj.filters │ ├── debug_logger_interface-15.vcxproj.filters │ ├── debug_logger_interface-16.vcxproj.filters │ ├── debug_logger_interface-17.vcxproj.filters │ ├── advanced_event_handling-14.vcxproj.filters │ ├── advanced_event_handling-15.vcxproj.filters │ ├── advanced_event_handling-16.vcxproj.filters │ ├── advanced_event_handling-17.vcxproj.filters │ ├── advanced_event_handling-clang.vcxproj.filters │ ├── shared-clang-debug.props │ ├── shared-clang-release.props │ ├── doctest.props │ ├── shared-vs-15.props │ ├── debug_logger_interface-clang.vcxproj.filters │ ├── shared-clang.props │ ├── shared-vs.props │ └── tools.pyproj ├── premake.cmd ├── .editorconfig ├── assets ├── logos │ ├── hfsm2-logo-jekyll.png │ └── hfsm2-logo-large.png └── css │ └── style.scss ├── test ├── main.cpp ├── wiki_class_member.hpp ├── shared │ ├── test_random.cpp │ ├── test_bit_stream.cpp │ ├── test_bit_array.cpp │ └── test_task_list.cpp ├── wiki_serialization.cpp ├── wiki_class_member.cpp ├── test_query.cpp ├── test_manual_activation.cpp ├── reported │ ├── issue_49.cpp │ └── resuming_plan.cpp ├── test_contexts_random.cpp ├── test_composite_bst.cpp ├── test_access.cpp ├── test_react_order.cpp └── test_orthogonal_root.cpp ├── _config.yml ├── .gitignore ├── .github ├── dependabot.yml ├── FUNDING.yml └── workflows │ ├── flawfinder-analysis.yml │ ├── cmake-qemu-arm.yml │ ├── msbuild.yml │ ├── msvc-analysis.yml │ └── cmake.yml ├── development └── hfsm2 │ ├── detail │ ├── root │ │ ├── control_4.inl │ │ ├── control_5.hpp │ │ ├── plan_2.inl │ │ ├── core.hpp │ │ ├── control_2.inl │ │ ├── control_0.inl │ │ ├── core.inl │ │ ├── plan_0.inl │ │ ├── control_1.inl │ │ ├── plan_0.hpp │ │ ├── registry_2.hpp │ │ ├── control_4.hpp │ │ └── control_2.hpp │ ├── features │ │ ├── task_list.hpp │ │ ├── structure_report.hpp │ │ ├── task.hpp │ │ ├── logger_interface.hpp │ │ └── task_list.inl │ ├── shared │ │ ├── iterator.hpp │ │ ├── bit_stream.hpp │ │ ├── bit_stream.inl │ │ ├── type_list.hpp │ │ └── macros_off.hpp │ ├── structure │ │ ├── ancestors_1.hpp │ │ ├── base.hpp │ │ ├── ancestors_2.inl │ │ ├── ancestors_1.inl │ │ └── ancestors_2.hpp │ ├── root_3.inl │ ├── containers │ │ ├── array.inl │ │ └── bit_array.hpp │ └── root_4.hpp │ └── machine_dev.hpp ├── CMakePresets.json ├── examples ├── temp │ ├── CMakeLists.txt │ └── main.cpp ├── calculator │ └── CMakeLists.txt ├── basic_audio_player │ ├── CMakeLists.txt │ └── main.cpp ├── basic_traffic_light │ └── CMakeLists.txt ├── debug_logger_interface │ └── CMakeLists.txt └── advanced_event_handling │ └── CMakeLists.txt ├── LICENSE ├── tools └── join.py ├── CMake └── modules │ └── coverage.cmake └── CMakeLists.txt /CNAME: -------------------------------------------------------------------------------- 1 | hfsm.dev -------------------------------------------------------------------------------- /projects/code-lite/test/tags: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /premake.cmd: -------------------------------------------------------------------------------- 1 | premake5.exe --file=premake.lua vs2022 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = tab 5 | indent_size = 4 6 | insert_final_newline = true 7 | -------------------------------------------------------------------------------- /assets/logos/hfsm2-logo-jekyll.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-gresyk/HFSM2/HEAD/assets/logos/hfsm2-logo-jekyll.png -------------------------------------------------------------------------------- /assets/logos/hfsm2-logo-large.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrew-gresyk/HFSM2/HEAD/assets/logos/hfsm2-logo-large.png -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 5 | #include 6 | -------------------------------------------------------------------------------- /projects/premake/temp-14.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/premake/temp-15.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/premake/temp-16.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/premake/temp-17.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/premake/temp-clang.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/visual-studio/shared-vs-14.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/visual-studio/vs-flags.txt: -------------------------------------------------------------------------------- 1 | /d1reportAllClassLayout 2 | /d1reportSingleClassLayoutNAME 3 | 4 | /Bt 5 | 6 | /d2cgsummary (compiler) 7 | /d2:-cgsummary (linker) 8 | 9 | /cgthreads# 10 | 11 | /d2FuncCache# (compiler) 12 | /d2:-FuncCache# (linker) 13 | 14 | /d2:-notypeopt (linker) -------------------------------------------------------------------------------- /projects/premake/snippets-14.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/premake/snippets-15.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/premake/snippets-16.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/premake/snippets-17.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /projects/premake/snippets-clang.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "{{ site.theme }}"; 5 | 6 | a:link, a:visited { 7 | color: #0057b7; 8 | font-weight: bold; 9 | text-decoration: none; 10 | } 11 | a:hover, a:active { 12 | background-color: #ffd700; 13 | color: #0057b7; 14 | font-weight: bold; 15 | text-decoration: none; 16 | } 17 | -------------------------------------------------------------------------------- /projects/visual-studio/temp-14.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/temp-15.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/temp-16.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/temp-17.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/temp-clang.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/calculator-14.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/calculator-15.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/calculator-16.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/calculator-17.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | title: HFSM2 2 | description: High-Performance Hierarchical Finite State Machine Framework 3 | url: "http://hfsm.dev" 4 | twitter_username: andrew_gresyk 5 | github_username: andrew-gresyk 6 | google_analytics: G-8CBZLEMT7W 7 | 8 | theme: jekyll-theme-minimal # https://github.com/pages-themes/minimal / https://pages.github.com/themes/ 9 | logo: assets/logos/hfsm2-logo-jekyll.png 10 | -------------------------------------------------------------------------------- /projects/visual-studio/calculator-clang.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_audio_player-14.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_audio_player-15.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_audio_player-16.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_audio_player-17.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_audio_player-clang.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_traffic_light-14.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_traffic_light-15.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_traffic_light-16.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_traffic_light-17.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_traffic_light-clang.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/debug_logger_interface-14.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/debug_logger_interface-15.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/debug_logger_interface-16.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/debug_logger_interface-17.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/advanced_event_handling-14.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/advanced_event_handling-15.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/advanced_event_handling-16.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/advanced_event_handling-17.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/advanced_event_handling-clang.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/shared-clang-debug.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | DebugFull 9 | 10 | 11 | -------------------------------------------------------------------------------- /projects/visual-studio/shared-clang-release.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | false 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .cache 2 | .codelite 3 | .idea 4 | .vs 5 | .vscode 6 | build 7 | cmake-* 8 | 9 | *.user 10 | 11 | /binaries* 12 | /build* 13 | /hfsm_test.dir 14 | /projects/code-lite/build-* 15 | /projects/code-lite/compile_commands.json 16 | /projects/code-lite/tags 17 | /projects/code-lite/Makefile 18 | /projects/code-lite/test/compile_flags.txt 19 | /projects/code-lite/test/test.mk 20 | /projects/visual-studio/.opencppcov 21 | /projects/visual-studio/*.log 22 | /pull.cmd 23 | /push.cmd 24 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "weekly" 12 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/control_4.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | HFSM2_CONSTEXPR(14) 8 | void 9 | GuardControlT::cancelPendingTransitions() noexcept { 10 | _cancelled = true; 11 | 12 | HFSM2_LOG_CANCELLED_PENDING(context(), _originId); 13 | } 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /projects/visual-studio/doctest.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | $(SolutionDir)../../external;%(AdditionalIncludeDirectories) 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /projects/visual-studio/shared-vs-15.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | /permissive- 10 | stdcpp14 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "configurePresets": [ 4 | { 5 | "name": "debug", 6 | "displayName": "Debug", 7 | "generator": "Ninja", 8 | "cacheVariables": { 9 | "CMAKE_BUILD_TYPE": "Debug", 10 | "HFSM2_BUILD_TESTS": "ON", 11 | "HFSM2_BUILD_EXAMPLES": "ON" 12 | } 13 | }, 14 | { 15 | "name": "release", 16 | "displayName": "Release", 17 | "generator": "Ninja", 18 | "cacheVariables": { 19 | "CMAKE_BUILD_TYPE": "Release" 20 | } 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /projects/code-lite/hfsm2.workspace: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /projects/visual-studio/debug_logger_interface-clang.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {f36e39b3-d6a6-4d9f-be7c-7081167a0b62} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/temp/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | get_filename_component(EXAMPLE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 4 | project(${EXAMPLE_NAME}_example) 5 | 6 | file(GLOB SOURCE_FILES "*.cpp") 7 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 8 | 9 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../include) 10 | 11 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) 12 | 13 | if(MSVC) 14 | target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) 15 | else() 16 | target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror -pedantic) 17 | endif() 18 | -------------------------------------------------------------------------------- /examples/calculator/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | get_filename_component(EXAMPLE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 4 | project(${EXAMPLE_NAME}_example) 5 | 6 | file(GLOB SOURCE_FILES "*.cpp") 7 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 8 | 9 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../include) 10 | 11 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) 12 | 13 | if(MSVC) 14 | target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) 15 | else() 16 | target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror -pedantic) 17 | endif() 18 | -------------------------------------------------------------------------------- /examples/basic_audio_player/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | get_filename_component(EXAMPLE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 4 | project(${EXAMPLE_NAME}_example) 5 | 6 | file(GLOB SOURCE_FILES "*.cpp") 7 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 8 | 9 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../include) 10 | 11 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11) 12 | 13 | if(MSVC) 14 | target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) 15 | else() 16 | target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror -pedantic) 17 | endif() 18 | -------------------------------------------------------------------------------- /examples/basic_traffic_light/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | get_filename_component(EXAMPLE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 4 | project(${EXAMPLE_NAME}_example) 5 | 6 | file(GLOB SOURCE_FILES "*.cpp") 7 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 8 | 9 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../include) 10 | 11 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11) 12 | 13 | if(MSVC) 14 | target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) 15 | else() 16 | target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror -pedantic) 17 | endif() 18 | -------------------------------------------------------------------------------- /examples/debug_logger_interface/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | get_filename_component(EXAMPLE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 4 | project(${EXAMPLE_NAME}_example) 5 | 6 | file(GLOB SOURCE_FILES "*.cpp") 7 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 8 | 9 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../include) 10 | 11 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11) 12 | 13 | if(MSVC) 14 | target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) 15 | else() 16 | target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror -pedantic) 17 | endif() 18 | -------------------------------------------------------------------------------- /projects/visual-studio/shared-clang.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -Wshadow -Wold-style-cast -Wpedantic %(AdditionalOptions) 9 | false 10 | 11 | 12 | Default 13 | 14 | 15 | -------------------------------------------------------------------------------- /examples/advanced_event_handling/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | get_filename_component(EXAMPLE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 4 | project(${EXAMPLE_NAME}_example) 5 | 6 | file(GLOB SOURCE_FILES "*.cpp") 7 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 8 | 9 | target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../include) 10 | 11 | target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_11) 12 | 13 | if(MSVC) 14 | target_compile_options(${PROJECT_NAME} PRIVATE /W4 /WX) 15 | else() 16 | target_compile_options(${PROJECT_NAME} PRIVATE -Wall -Wextra -Werror -pedantic) 17 | endif() 18 | -------------------------------------------------------------------------------- /test/wiki_class_member.hpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #pragma once 5 | 6 | #include // max_align_t 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | class Actor { 11 | public: 12 | struct alignas(std::max_align_t) FsmHost { 13 | char buffer[104]; // the size is hand-adjusted 14 | }; 15 | 16 | public: 17 | Actor(); 18 | ~Actor(); 19 | 20 | void turnOn(); 21 | 22 | bool isOn() const; 23 | 24 | private: 25 | FsmHost _fsmHost; 26 | }; 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: andrew-gresyk 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /projects/visual-studio/shared-vs.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | $(SolutionDir)..\..\binaries-$(PlatformTarget)\ 6 | $(BUILD_ROOT)\$(SolutionName)-$(PlatformTarget)\$(ProjectName)-$(Configuration)\ 7 | $(ProjectName)-$(Configuration)-$(PlatformTarget) 8 | 9 | 10 | 11 | $(SolutionDir)../../development;$(SolutionDir)../../include;%(AdditionalIncludeDirectories) 12 | Level4 13 | true 14 | _UNICODE;UNICODE;_CONSOLE;%(PreprocessorDefinitions) 15 | 16 | 17 | Console 18 | 19 | 20 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/control_5.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | class EventControlT final 8 | : public FullControlT 9 | { 10 | template 11 | friend class R_; 12 | 13 | template 14 | friend struct PreReactWrapperT; 15 | 16 | template 17 | friend struct ReactWrapperT; 18 | 19 | template 20 | friend struct PostReactWrapperT; 21 | 22 | using FullControl = FullControlT; 23 | 24 | using FullControl::FullControl; 25 | 26 | public: 27 | using FullControl::context; 28 | 29 | /// @brief Stops processing of the current event down the hierarchy 30 | /// @see `Config::BottomUpReactions` 31 | HFSM2_CONSTEXPR(14) void consumeEvent() noexcept { _consumed = true; } 32 | 33 | private: 34 | bool _consumed = false; 35 | }; 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/plan_2.inl: -------------------------------------------------------------------------------- 1 | #if HFSM2_PLANS_AVAILABLE() 2 | 3 | namespace hfsm2 { 4 | namespace detail { 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | template 9 | HFSM2_CONSTEXPR(14) 10 | bool 11 | PayloadPlanT>:: append(const StateID origin, 12 | const StateID destination, 13 | const TransitionType transitionType, 14 | const Payload& payload) noexcept 15 | { 16 | if (_planData.tasks.count() < TASK_CAPACITY) { 17 | _planData.planExists.set(_regionId); 18 | 19 | return linkTask(_planData.tasks.emplace(origin, destination, transitionType, payload)); 20 | } else 21 | return false; 22 | } 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | } 27 | } 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Andrew Gresyk 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/flawfinder-analysis.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | 6 | name: flawfinder 7 | 8 | on: 9 | push: 10 | branches: [ master ] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [ master ] 14 | schedule: 15 | - cron: '21 21 * * 6' 16 | 17 | jobs: 18 | flawfinder: 19 | name: Flawfinder 20 | runs-on: ubuntu-latest 21 | permissions: 22 | actions: read 23 | contents: read 24 | security-events: write 25 | steps: 26 | - name: Checkout code 27 | uses: actions/checkout@v6 28 | 29 | - name: flawfinder_scan 30 | uses: david-a-wheeler/flawfinder@c57197cd6061453f10a496f30a732bc1905918d1 31 | with: 32 | arguments: '--sarif ./' 33 | output: 'flawfinder_results.sarif' 34 | 35 | - name: Upload analysis results to GitHub Security tab 36 | uses: github/codeql-action/upload-sarif@v4 37 | with: 38 | sarif_file: ${{github.workspace}}/flawfinder_results.sarif 39 | -------------------------------------------------------------------------------- /.github/workflows/cmake-qemu-arm.yml: -------------------------------------------------------------------------------- 1 | name: CMake QEMU ARM 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | env: 14 | CC: arm-linux-gnueabihf-gcc 15 | CXX: arm-linux-gnueabihf-g++ 16 | AR: arm-linux-gnueabihf-ar 17 | LD: arm-linux-gnueabihf-ld 18 | LDFLAGS: -L/usr/arm-linux-gnueabihf/lib 19 | CMAKE_CROSSCOMPILING: True 20 | QEMU_LD_PREFIX: /usr/arm-linux-gnueabihf 21 | CMAKE_CROSSCOMPILING_EMULATOR: qemu-arm 22 | 23 | strategy: 24 | matrix: 25 | BUILD_CONFIG: [ Release, Debug ] 26 | 27 | steps: 28 | - uses: actions/checkout@v6 29 | 30 | - name: Install ARM GCC on ubuntu 31 | run: | 32 | sudo apt-get update 33 | sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf qemu-user 34 | 35 | - name: Configure 36 | run: cmake -B ./build -DHFSM2_BUILD_TESTS=ON -DHFSM2_BUILD_EXAMPLES=ON -DCMAKE_BUILD_TYPE=${{ matrix.BUILD_CONFIG }} 37 | working-directory: ${{ github.workspace }} 38 | 39 | - name: Build 40 | run: cmake --build ./build --config ${{ matrix.BUILD_CONFIG }} 41 | working-directory: ${{ github.workspace }} 42 | -------------------------------------------------------------------------------- /.github/workflows/msbuild.yml: -------------------------------------------------------------------------------- 1 | name: MSBuild 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.PLATFORM.OS }} 12 | 13 | env: 14 | SOLUTION_FILE_PATH: ${{ matrix.PLATFORM.SOLUTION_FILE_PATH }} 15 | PROJECT_FILE_NAME: ${{ matrix.PLATFORM.PROJECT_FILE_NAME }} 16 | 17 | strategy: 18 | matrix: 19 | PLATFORM: 20 | - { 21 | OS: windows-2022, 22 | SOLUTION_FILE_PATH: projects\premake\hfsm2-vs-2022.sln, 23 | PROJECT_FILE_NAME: test-17 24 | } 25 | 26 | BUILD_CONFIG: [ Release, Debug ] 27 | BUILD_PLATFORM: [ Win32, x64 ] 28 | 29 | steps: 30 | - uses: actions/checkout@v6 31 | 32 | - name: Add MSBuild to PATH 33 | uses: microsoft/setup-msbuild@main 34 | 35 | - name: Build 36 | working-directory: ${{env.GITHUB_WORKSPACE}} 37 | run: msbuild /m /p:Configuration=${{matrix.BUILD_CONFIG}} /p:Platform=${{matrix.BUILD_PLATFORM}} ${{env.SOLUTION_FILE_PATH}} 38 | 39 | - name: Test 40 | working-directory: ${{env.GITHUB_WORKSPACE}} 41 | run: "binaries/${{env.PROJECT_FILE_NAME}}-${{matrix.BUILD_CONFIG}}-${{matrix.BUILD_PLATFORM}}.exe" 42 | -------------------------------------------------------------------------------- /test/shared/test_random.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_UTILITY_THEORY 5 | #include "../tools.hpp" 6 | 7 | #include 8 | 9 | namespace test_random { 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | template 14 | void 15 | testUniformity(const int average) { 16 | using Type = T; 17 | using Random = hfsm2::RNGT; 18 | 19 | Random random{0}; 20 | int histogram[10] = {0}; 21 | 22 | for (int i = 0; i < 10 * average; ++i) { 23 | const float real = random.next(); 24 | REQUIRE(real >= Type{0.0}); 25 | REQUIRE(real < Type{1.0}); 26 | 27 | const unsigned n = static_cast(10.0f * real); 28 | REQUIRE(n < hfsm2::count(histogram)); 29 | 30 | ++histogram[n]; 31 | } 32 | 33 | for (unsigned i = 1; i < hfsm2::count(histogram); ++i) { 34 | const int delta = abs(average - histogram[i]); 35 | const float relative = 1.0f * delta / average; 36 | 37 | REQUIRE(relative < 0.2f); 38 | } 39 | } 40 | 41 | //------------------------------------------------------------------------------ 42 | 43 | TEST_CASE("Shared.RandomT<>") { 44 | testUniformity(100); 45 | } 46 | 47 | //////////////////////////////////////////////////////////////////////////////// 48 | 49 | } 50 | -------------------------------------------------------------------------------- /test/shared/test_bit_stream.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_SERIALIZATION 5 | #include "../tools.hpp" 6 | 7 | namespace test_bit_stream { 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | using WriteStream = hfsm2::detail::BitWriteStreamT<45>; 12 | using ReadStream = hfsm2::detail::BitReadStreamT <45>; 13 | using StreamBuffer = typename WriteStream::Buffer; 14 | 15 | //------------------------------------------------------------------------------ 16 | 17 | TEST_CASE("Shared.BitStream<>") { 18 | StreamBuffer buffer; 19 | 20 | WriteStream writeStream{buffer}; 21 | writeStream.write< 5>(static_cast( 27)); 22 | writeStream.write< 4>(static_cast( 11)); 23 | writeStream.write< 3>(static_cast( 5)); 24 | writeStream.write<12>(static_cast( 1472)); 25 | writeStream.write<21>(static_cast(1000000)); 26 | REQUIRE(writeStream.cursor() == 45); 27 | 28 | ReadStream readStream{buffer}; 29 | REQUIRE(readStream.read< 5>() == 27); 30 | REQUIRE(readStream.read< 4>() == 11); 31 | REQUIRE(readStream.read< 3>() == 5); 32 | REQUIRE(readStream.read<12>() == 1472); 33 | REQUIRE(readStream.read<21>() == 1000000); 34 | REQUIRE(readStream.cursor() == 45); 35 | } 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | 39 | } 40 | -------------------------------------------------------------------------------- /test/wiki_serialization.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_SERIALIZATION 5 | #include 6 | 7 | #include 8 | 9 | using M = hfsm2::Machine; 10 | 11 | using FSM = M::PeerRoot< 12 | struct State1, 13 | struct State2 14 | >; 15 | 16 | struct State1 : FSM::State { /* .. */ }; 17 | struct State2 : FSM::State { /* .. */ }; 18 | 19 | //------------------------------------------------------------------------------ 20 | 21 | TEST_CASE("Docs.Serialization") { 22 | // Buffer for serialization 23 | // Members: 24 | // bitSize - Number of payload bits used 25 | // payload - Serialized data 26 | FSM::Instance::SerialBuffer buffer; 27 | 28 | { 29 | FSM::Instance fsm; // Create a new FSM instance 30 | fsm.immediateChangeTo(); // Transition to 'State2' 31 | REQUIRE(fsm.isActive()); // Check if transition completed 32 | 33 | fsm.save(buffer); // Serialize FSM configuration into 'buffer' 34 | } 35 | 36 | { 37 | FSM::Instance fsm; // Create a fresh FSM instance 38 | REQUIRE(fsm.isActive()); // Initial 'State1' is activated by default 39 | 40 | fsm.load(buffer); // De-serialize FSM from 'buffer' 41 | REQUIRE(fsm.isActive()); // Check its configuration is restored 42 | } 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | -------------------------------------------------------------------------------- /development/hfsm2/detail/features/task_list.hpp: -------------------------------------------------------------------------------- 1 | #if HFSM2_PLANS_AVAILABLE() 2 | 3 | namespace hfsm2 { 4 | namespace detail { 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | template 9 | class TaskListT { 10 | public: 11 | using Index = Long; 12 | 13 | static constexpr Index CAPACITY = NCapacity; 14 | static constexpr Index INVALID = Index (-1); 15 | 16 | private: 17 | using Payload = TPayload; 18 | using Item = TaskT; 19 | 20 | public: 21 | HFSM2_CONSTEXPR(14) void clear() noexcept; 22 | 23 | template 24 | HFSM2_CONSTEXPR(14) Index emplace(TArgs&&... args) noexcept; 25 | 26 | HFSM2_CONSTEXPR(14) void remove(const Index i) noexcept; 27 | 28 | HFSM2_CONSTEXPR(14) Item& operator[] (const Index i) noexcept; 29 | HFSM2_CONSTEXPR(11) const Item& operator[] (const Index i) const noexcept; 30 | 31 | HFSM2_CONSTEXPR(11) Index count() const noexcept { return _count; } 32 | HFSM2_CONSTEXPR(11) bool empty() const noexcept { return _count == 0; } 33 | 34 | private: 35 | HFSM2_IF_ASSERT(void verifyStructure(const Index occupied = INVALID) const noexcept); 36 | 37 | private: 38 | Index _vacantHead = 0; 39 | Index _vacantTail = 0; 40 | Index _last = 0; 41 | Index _count = 0; 42 | Item _items[CAPACITY] {}; 43 | }; 44 | 45 | //------------------------------------------------------------------------------ 46 | 47 | template 48 | class TaskListT {}; 49 | 50 | //////////////////////////////////////////////////////////////////////////////// 51 | 52 | } 53 | } 54 | 55 | #include "task_list.inl" 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /development/hfsm2/detail/features/structure_report.hpp: -------------------------------------------------------------------------------- 1 | #if HFSM2_STRUCTURE_REPORT_AVAILABLE() 2 | 3 | namespace hfsm2 { 4 | 5 | //------------------------------------------------------------------------------ 6 | 7 | struct StructureEntry final { 8 | HFSM2_CONSTEXPR(11) 9 | StructureEntry(const bool isActive_ = false, 10 | const wchar_t* const prefix_ = nullptr, 11 | const char * const name_ = nullptr) noexcept 12 | : isActive{isActive_} 13 | , prefix {prefix_ } 14 | , name {name_ } 15 | {} 16 | 17 | bool isActive; 18 | const wchar_t* prefix; 19 | const char * name; 20 | }; 21 | 22 | namespace detail { 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | enum class RegionType { 27 | COMPOSITE, 28 | ORTHOGONAL, 29 | 30 | COUNT 31 | }; 32 | 33 | //------------------------------------------------------------------------------ 34 | 35 | struct StructureStateInfo final { 36 | StructureStateInfo() noexcept = default; 37 | 38 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 39 | 40 | HFSM2_CONSTEXPR(11) 41 | StructureStateInfo(const Long parent_, 42 | const RegionType regionType_, 43 | const Short depth_, 44 | const char* const name_) noexcept 45 | : name{name_} 46 | , parent{parent_} 47 | , regionType{regionType_ } 48 | , depth{depth_} 49 | {} 50 | 51 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 52 | 53 | const char* name = nullptr; 54 | Long parent = INVALID_LONG; 55 | RegionType regionType = RegionType::COUNT; 56 | Short depth = INVALID_SHORT; 57 | }; 58 | 59 | //////////////////////////////////////////////////////////////////////////////// 60 | 61 | } 62 | } 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /tools/join.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | #=============================================================================== 4 | 5 | def merge(path, folder, lastLineEmpty, included, output, commentRE): 6 | pathTokens = path.split("/") 7 | 8 | current = folder + "/" + pathTokens[-1] 9 | with open(current, 'r', encoding='utf-8') as input: 10 | if not lastLineEmpty: 11 | output.write("\n") 12 | lastLineEmpty = True 13 | 14 | for line in input: 15 | hashIndex = line.find('#include "') 16 | 17 | if hashIndex != -1: 18 | next = line[hashIndex + 10 : -2] 19 | 20 | if next not in included: 21 | nextTokens = next.split("/") 22 | included.append(nextTokens[-1]) 23 | 24 | #output.write("// inlined '" + pathTokens[-1] + "' -> '" + nextTokens[-1] + "'\n") 25 | 26 | if len(nextTokens) == 1: 27 | lastLineEmpty = merge(next, folder, lastLineEmpty, included, output, commentRE) 28 | else: 29 | name = nextTokens.pop() 30 | subFolder = folder + "/" + "/".join(nextTokens) 31 | lastLineEmpty = merge(name, subFolder, lastLineEmpty, included, output, commentRE) 32 | 33 | else: 34 | if line.startswith('\ufeff'): 35 | line = line[1:] 36 | 37 | if commentRE.match(line): 38 | continue 39 | 40 | if line == "\n": 41 | if lastLineEmpty: 42 | continue 43 | 44 | lastLineEmpty = True 45 | else: 46 | lastLineEmpty = False 47 | 48 | output.write(line) 49 | 50 | return lastLineEmpty 51 | 52 | #------------------------------------------------------------------------------- 53 | 54 | commentRE = re.compile("(?:\s*\/\/ COMMON)|(?:\s*\/\/ SPECIFIC)|(?:\s*\/\/\/\/)|(?:\s*\/\/--)|(?:\s*\/\/ -)") 55 | 56 | output = open("../include/hfsm2/machine.hpp" , 'w', encoding='utf-8-sig') 57 | merge("machine_dev.hpp" , "../development/hfsm2", True, [], output, commentRE) 58 | output.close() 59 | 60 | #=============================================================================== 61 | -------------------------------------------------------------------------------- /.github/workflows/msvc-analysis.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. 2 | # They are provided by a third-party and are governed by 3 | # separate terms of service, privacy policy, and support 4 | # documentation. 5 | # 6 | # Find more information at: 7 | # https://github.com/microsoft/msvc-code-analysis-action 8 | 9 | name: Microsoft C++ Code Analysis 10 | 11 | on: 12 | push: 13 | branches: [ master ] 14 | pull_request: 15 | branches: [ master ] 16 | schedule: 17 | - cron: '22 3 * * 3' 18 | 19 | env: 20 | # Path to the CMake build directory. 21 | build: '${{ github.workspace }}/build' 22 | 23 | jobs: 24 | analyze: 25 | name: Analyze 26 | runs-on: windows-latest 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v6 31 | 32 | - name: Configure CMake 33 | run: cmake -B ${{ env.build }} 34 | 35 | # Build is not required unless generated source files are used 36 | # - name: Build CMake 37 | # run: cmake --build ${{ env.build }} 38 | 39 | - name: Initialize MSVC Code Analysis 40 | uses: microsoft/msvc-code-analysis-action@24c285ab36952c9e9182f4b78dfafbac38a7e5ee 41 | # Provide a unique ID to access the sarif output path 42 | id: run-analysis 43 | with: 44 | cmakeBuildDirectory: ${{ env.build }} 45 | # Ruleset file that will determine what checks will be run 46 | ruleset: NativeRecommendedRules.ruleset 47 | 48 | # Upload SARIF file to GitHub Code Scanning Alerts 49 | - name: Upload SARIF to GitHub 50 | uses: github/codeql-action/upload-sarif@v4 51 | with: 52 | sarif_file: ${{ steps.run-analysis.outputs.sarif }} 53 | 54 | # Upload SARIF file as an Artifact to download and view 55 | # - name: Upload SARIF as an Artifact 56 | # uses: actions/upload-artifact@v2 57 | # with: 58 | # name: sarif-file 59 | # path: ${{ steps.run-analysis.outputs.sarif }} 60 | -------------------------------------------------------------------------------- /projects/premake/tools.pyproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Debug 4 | 2.0 5 | 2347027e-e5de-43c4-8ef9-5f1411ade33b 6 | . 7 | ..\..\tools\join.py 8 | 9 | 10 | ../../tools 11 | Global|PythonCore|3.9 12 | Standard Python launcher 13 | . 14 | HFSM-tools 15 | HFSM-tools 16 | False 17 | False 18 | 19 | 20 | true 21 | false 22 | 23 | 24 | true 25 | false 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /projects/visual-studio/tools.pyproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Debug 4 | 2.0 5 | 2347027e-e5de-43c4-8ef9-5f1411ade33b 6 | . 7 | ..\..\tools\join.py 8 | 9 | 10 | ../../tools 11 | Global|PythonCore|3.9 12 | Standard Python launcher 13 | . 14 | HFSM-tools 15 | HFSM-tools 16 | False 17 | False 18 | 19 | 20 | true 21 | false 22 | 23 | 24 | true 25 | false 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/core.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | struct CoreT { 8 | using Context = typename TArgs::Context; 9 | using PureContext = typename TArgs::PureContext; 10 | 11 | using Registry = RegistryT; 12 | 13 | using Payload = typename TArgs::Payload; 14 | using Transition = TransitionT; 15 | using TransitionSet = DynamicArrayT; 16 | using TransitionSets = DynamicArrayT; 17 | 18 | #if HFSM2_PLANS_AVAILABLE() 19 | using PlanData = PlanDataT; 20 | #endif 21 | 22 | #if HFSM2_TRANSITION_HISTORY_AVAILABLE() 23 | using TransitionTargets = StaticArrayT; 24 | #endif 25 | 26 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 27 | using RNG = typename TArgs::RNG; 28 | #endif 29 | 30 | #if HFSM2_LOG_INTERFACE_AVAILABLE() 31 | using Logger = typename TArgs::Logger; 32 | #endif 33 | 34 | HFSM2_CONSTEXPR(14) explicit CoreT(Context& context_ 35 | HFSM2_IF_UTILITY_THEORY(, RNG& rng_) 36 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger_ = nullptr)) noexcept; 37 | 38 | HFSM2_CONSTEXPR(14) explicit CoreT(PureContext&& context_ 39 | HFSM2_IF_UTILITY_THEORY(, RNG& rng_) 40 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger_ = nullptr)) noexcept; 41 | 42 | HFSM2_CONSTEXPR(14) CoreT(const CoreT& other) noexcept; 43 | HFSM2_CONSTEXPR(14) CoreT( CoreT&& other) noexcept; 44 | 45 | Context context; 46 | Registry registry; 47 | TransitionSet requests; 48 | HFSM2_IF_PLANS(PlanData planData); 49 | HFSM2_IF_TRANSITION_HISTORY(TransitionTargets transitionTargets); 50 | HFSM2_IF_TRANSITION_HISTORY(TransitionSets previousTransitions); 51 | HFSM2_IF_UTILITY_THEORY(RNG& rng); 52 | HFSM2_IF_LOG_INTERFACE(Logger* logger); 53 | }; 54 | 55 | //////////////////////////////////////////////////////////////////////////////// 56 | 57 | } 58 | } 59 | 60 | #include "core.inl" 61 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/control_2.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | HFSM2_CONSTEXPR(14) 8 | PlanControlT::Region::Region(PlanControlT& control_, 9 | const RegionID regionId_, 10 | const StateID index, 11 | const Long size) noexcept 12 | : control {control_} 13 | , prevId {control._regionId} 14 | , prevIndex{control._regionStateId} 15 | , prevSize {control._regionSize} 16 | { 17 | control.setRegion(regionId_, index, size); 18 | } 19 | 20 | //------------------------------------------------------------------------------ 21 | 22 | template 23 | HFSM2_CONSTEXPR(20) 24 | PlanControlT::Region::~Region() noexcept { 25 | control.resetRegion(prevId, prevIndex, prevSize); 26 | } 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | 30 | template 31 | HFSM2_CONSTEXPR(14) 32 | void 33 | PlanControlT::setRegion(const RegionID regionId_, 34 | const StateID index, 35 | const Long size) noexcept 36 | { 37 | HFSM2_ASSERT(_regionId <= regionId_ && regionId_ < RegionList::SIZE); 38 | HFSM2_ASSERT(_regionStateId <= index && index + size <= _regionStateId + _regionSize); 39 | 40 | _regionId = regionId_; 41 | _regionStateId = index; 42 | _regionSize = size; 43 | } 44 | 45 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 46 | 47 | template 48 | HFSM2_CONSTEXPR(14) 49 | void 50 | PlanControlT::resetRegion(const RegionID regionId_, 51 | const StateID index, 52 | const Long size) noexcept 53 | { 54 | HFSM2_ASSERT(regionId_ <= _regionId && _regionId < RegionList::SIZE); 55 | HFSM2_ASSERT(index <= _regionStateId && _regionStateId + _regionSize <= index + size); 56 | 57 | _regionId = regionId_; 58 | _regionStateId = index; 59 | _regionSize = size; 60 | 61 | _taskStatus.clear(); 62 | } 63 | 64 | //////////////////////////////////////////////////////////////////////////////// 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/control_0.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | HFSM2_CONSTEXPR(14) 8 | ConstControlT::Origin::Origin(ConstControlT& control_, 9 | const StateID stateId_) noexcept 10 | : control{control_} 11 | , prevId{control._originId} 12 | { 13 | HFSM2_ASSERT(stateId_ < StateList::SIZE); 14 | control._originId = stateId_; 15 | } 16 | 17 | //------------------------------------------------------------------------------ 18 | 19 | template 20 | HFSM2_CONSTEXPR(20) 21 | ConstControlT::Origin::~Origin() noexcept { 22 | control._originId = prevId; 23 | } 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | template 28 | HFSM2_CONSTEXPR(14) 29 | ConstControlT::Region::Region(ConstControlT& control_, 30 | const RegionID regionId_) noexcept 31 | : control{control_} 32 | , prevId{control._regionId} 33 | { 34 | control.setRegion(regionId_); 35 | } 36 | 37 | //------------------------------------------------------------------------------ 38 | 39 | template 40 | HFSM2_CONSTEXPR(20) 41 | ConstControlT::Region::~Region() noexcept { 42 | control.resetRegion(prevId); 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | template 48 | HFSM2_CONSTEXPR(14) 49 | void 50 | ConstControlT::setRegion(const RegionID regionId_) noexcept { 51 | HFSM2_ASSERT(_regionId <= regionId_ && regionId_ < RegionList::SIZE); 52 | 53 | _regionId = regionId_; 54 | } 55 | 56 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 57 | 58 | template 59 | HFSM2_CONSTEXPR(14) 60 | void 61 | ConstControlT::resetRegion(const RegionID regionId_) noexcept { 62 | HFSM2_ASSERT(regionId_ <= _regionId && _regionId < RegionList::SIZE); 63 | 64 | _regionId = regionId_; 65 | } 66 | 67 | //////////////////////////////////////////////////////////////////////////////// 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /examples/temp/main.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | struct Context {}; 12 | using Config = hfsm2::Config ::ContextT; 13 | using M = hfsm2::MachineT; 14 | 15 | using FSM = M::PeerRoot< 16 | struct Off, 17 | M::Composite 20 | >; 21 | 22 | struct CloseEvent {}; 23 | 24 | struct Off : FSM::State { 25 | void entryGuard(GuardControl& control) { 26 | printf("/Off entry guard: change to On\n"); 27 | control.changeTo(); 28 | } 29 | void enter(PlanControl& /*control*/) { printf(" /Off enter\n"); } 30 | void exitGuard(GuardControl& /*control*/) { printf("\\Off exit guard\n"); } 31 | void exit(PlanControl& /*control*/) { printf(" \\Off exit\n"); } 32 | }; 33 | 34 | struct Work : FSM::State { 35 | void entryGuard(GuardControl& /*control*/) { printf("/Work entry guard\n"); } 36 | void enter(PlanControl& /*control*/) { printf(" /Work enter\n"); } 37 | void exitGuard(GuardControl& /*control*/) { printf("\\Work exit guard\n"); } 38 | void exit(PlanControl& /*control*/) { printf(" \\Work exit\n"); } 39 | }; 40 | 41 | struct On : FSM::State { 42 | void entryGuard(GuardControl& /*control*/) { printf("/On entry guard\n"); } 43 | void enter(PlanControl& /*control*/) { printf(" /On enter\n"); } 44 | void reenter(PlanControl& /*control*/) { printf(" |On reenter\n"); } 45 | void exitGuard(GuardControl& /*control*/) { printf("\\On exit guard\n"); } 46 | void exit(PlanControl& /*control*/) { printf(" \\On exit\n"); } 47 | 48 | void react(const CloseEvent& /*event*/, EventControl& control) { 49 | printf(" -On react: change to Off\n"); 50 | control.changeTo(); 51 | } 52 | }; 53 | 54 | int main() { 55 | Context context; 56 | FSM::Instance machine{context}; 57 | printf("init finished\n"); 58 | 59 | machine.react(CloseEvent{}); 60 | printf("react finished\n"); 61 | 62 | return 0; 63 | } 64 | 65 | //////////////////////////////////////////////////////////////////////////////// 66 | -------------------------------------------------------------------------------- /test/wiki_class_member.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #include "wiki_class_member.hpp" 5 | 6 | #include 7 | 8 | #include 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | namespace actor_fsm { 13 | 14 | //------------------------------------------------------------------------------ 15 | 16 | using Config = hfsm2::Config 17 | ::ContextT; 18 | 19 | using M = hfsm2::MachineT; 20 | 21 | #define S(s) struct s 22 | using FSM = M::PeerRoot< 23 | S(Off), 24 | S(On) 25 | >; 26 | #undef S 27 | 28 | struct Off : FSM::State {}; 29 | struct On : FSM::State {}; 30 | 31 | FSM::Instance& 32 | fsm( Actor::FsmHost& fsmHost) { return *reinterpret_cast< FSM::Instance*>(&fsmHost); } 33 | 34 | const FSM::Instance& 35 | fsm(const Actor::FsmHost& fsmHost) { return *reinterpret_cast(&fsmHost); } 36 | 37 | //------------------------------------------------------------------------------ 38 | 39 | } 40 | 41 | Actor::Actor() { 42 | //hfsm2::StaticPrintConstT alignment; 43 | static_assert(alignof(actor_fsm::FSM::Instance) <= alignof(FsmHost), 44 | "Uncomment the line above to find out the alignment of the `FsmHost` needed"); 45 | 46 | //hfsm2::StaticPrintConstT size; 47 | static_assert(sizeof(actor_fsm::FSM::Instance) <= sizeof(FsmHost), 48 | "Uncomment the line above to find out the size of the `FsmHost` needed"); 49 | 50 | new (&_fsmHost) actor_fsm::FSM::Instance{*this}; 51 | } 52 | 53 | Actor::~Actor() { 54 | hfsm2::destroy(actor_fsm::fsm(_fsmHost)); 55 | } 56 | 57 | void Actor::turnOn() { 58 | actor_fsm::fsm(_fsmHost).immediateChangeTo(); 59 | } 60 | 61 | bool Actor::isOn() const { 62 | return actor_fsm::fsm(_fsmHost).isActive(); 63 | } 64 | 65 | //////////////////////////////////////////////////////////////////////////////// 66 | 67 | TEST_CASE("Wiki.Class Member") { 68 | Actor actor; 69 | REQUIRE(actor.isOn() == false); 70 | 71 | actor.turnOn(); 72 | REQUIRE(actor.isOn() == true); 73 | } 74 | 75 | //////////////////////////////////////////////////////////////////////////////// 76 | -------------------------------------------------------------------------------- /test/test_query.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #include "tools.hpp" 5 | 6 | using namespace test_tools; 7 | 8 | namespace test_query { 9 | 10 | //////////////////////////////////////////////////////////////////////////////// 11 | 12 | using M = hfsm2::Machine; 13 | 14 | //------------------------------------------------------------------------------ 15 | 16 | #define S(s) struct s 17 | 18 | using FSM = M::PeerRoot< 19 | S(A), 20 | S(B), 21 | S(C), 22 | S(D), 23 | S(E) 24 | >; 25 | 26 | #undef S 27 | 28 | //------------------------------------------------------------------------------ 29 | 30 | static_assert(FSM::stateId() == 1, ""); 31 | static_assert(FSM::stateId() == 2, ""); 32 | static_assert(FSM::stateId() == 3, ""); 33 | static_assert(FSM::stateId() == 4, ""); 34 | static_assert(FSM::stateId() == 5, ""); 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | 38 | template 39 | struct BaseT 40 | : public FSM::State 41 | { 42 | void query(hfsm2::StateID& stateId_, 43 | ConstControl&) const 44 | { 45 | stateId_ = stateId(); 46 | } 47 | }; 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | 51 | struct A : FSM::StateT> {}; 52 | struct B : FSM::StateT> {}; 53 | struct C : FSM::StateT> {}; 54 | struct D : FSM::StateT> {}; 55 | struct E : FSM::StateT> {}; 56 | 57 | //////////////////////////////////////////////////////////////////////////////// 58 | 59 | TEST_CASE("FSM.Query") { 60 | FSM::Instance machine; 61 | hfsm2::StateID stateId; 62 | 63 | machine.query(stateId); 64 | REQUIRE(stateId == FSM::stateId()); 65 | 66 | machine.immediateChangeTo(); 67 | machine.query(stateId); 68 | REQUIRE(stateId == FSM::stateId()); 69 | 70 | machine.immediateChangeTo(); 71 | machine.query(stateId); 72 | REQUIRE(stateId == FSM::stateId()); 73 | 74 | machine.immediateChangeTo(); 75 | machine.query(stateId); 76 | REQUIRE(stateId == FSM::stateId()); 77 | 78 | machine.immediateChangeTo(); 79 | machine.query(stateId); 80 | REQUIRE(stateId == FSM::stateId()); 81 | } 82 | 83 | //////////////////////////////////////////////////////////////////////////////// 84 | 85 | } 86 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/core.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | HFSM2_CONSTEXPR(14) 8 | CoreT::CoreT(Context& context_ 9 | HFSM2_IF_UTILITY_THEORY(, RNG& rng_) 10 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger_)) noexcept 11 | : context{context_} 12 | HFSM2_IF_UTILITY_THEORY(, rng{rng_}) 13 | HFSM2_IF_LOG_INTERFACE(, logger{logger_}) 14 | {} 15 | 16 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 17 | 18 | template 19 | HFSM2_CONSTEXPR(14) 20 | CoreT::CoreT(PureContext&& context_ 21 | HFSM2_IF_UTILITY_THEORY(, RNG& rng_) 22 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger_)) noexcept 23 | : context{move(context_)} 24 | HFSM2_IF_UTILITY_THEORY(, rng {rng_ }) 25 | HFSM2_IF_LOG_INTERFACE (, logger{logger_}) 26 | {} 27 | 28 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 29 | 30 | template 31 | HFSM2_CONSTEXPR(14) 32 | CoreT::CoreT(const CoreT& other) noexcept 33 | : context {other.context } 34 | , registry{other.registry} 35 | , requests{other.requests} 36 | HFSM2_IF_PLANS (, planData {other.planData }) 37 | HFSM2_IF_TRANSITION_HISTORY(, transitionTargets {other.transitionTargets }) 38 | HFSM2_IF_TRANSITION_HISTORY(, previousTransitions{other.previousTransitions}) 39 | HFSM2_IF_UTILITY_THEORY (, rng {other.rng }) 40 | HFSM2_IF_LOG_INTERFACE (, logger {other.logger }) 41 | {} 42 | 43 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 44 | 45 | template 46 | HFSM2_CONSTEXPR(14) 47 | CoreT::CoreT(CoreT&& other) noexcept 48 | : context {move(other.context )} 49 | , registry{move(other.registry)} 50 | , requests{move(other.requests)} 51 | HFSM2_IF_PLANS (, planData {move(other.planData )}) 52 | HFSM2_IF_TRANSITION_HISTORY(, transitionTargets {move(other.transitionTargets )}) 53 | HFSM2_IF_TRANSITION_HISTORY(, previousTransitions{move(other.previousTransitions)}) 54 | HFSM2_IF_UTILITY_THEORY (, rng {move(other.rng )}) 55 | HFSM2_IF_LOG_INTERFACE (, logger {move(other.logger )}) 56 | {} 57 | 58 | //////////////////////////////////////////////////////////////////////////////// 59 | 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/plan_0.inl: -------------------------------------------------------------------------------- 1 | #if HFSM2_PLANS_AVAILABLE() 2 | 3 | namespace hfsm2 { 4 | namespace detail { 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | template 9 | HFSM2_CONSTEXPR(14) 10 | CPlanT::Iterator::Iterator(const CPlanT& plan) noexcept 11 | : _plan{plan} 12 | , _curr{plan._bounds.first} 13 | { 14 | _next = next(); 15 | } 16 | 17 | //------------------------------------------------------------------------------ 18 | 19 | template 20 | HFSM2_CONSTEXPR(14) 21 | CPlanT::Iterator::operator bool() const noexcept { 22 | HFSM2_ASSERT(_curr < CPlanT::TASK_CAPACITY || 23 | _curr == INVALID_LONG); 24 | 25 | return _curr < CPlanT::TASK_CAPACITY; 26 | } 27 | 28 | //------------------------------------------------------------------------------ 29 | 30 | template 31 | HFSM2_CONSTEXPR(14) 32 | void 33 | CPlanT::Iterator::operator ++() noexcept { 34 | _curr = _next; 35 | _next = next(); 36 | } 37 | 38 | //------------------------------------------------------------------------------ 39 | 40 | template 41 | HFSM2_CONSTEXPR(14) 42 | Long 43 | CPlanT::Iterator::next() const noexcept { 44 | if (_curr < CPlanT::TASK_CAPACITY) { 45 | const TaskLink& link = _plan._planData.taskLinks[_curr]; 46 | 47 | return link.next; 48 | } else { 49 | HFSM2_ASSERT(_curr == INVALID_LONG); 50 | 51 | return INVALID_LONG; 52 | } 53 | } 54 | 55 | //////////////////////////////////////////////////////////////////////////////// 56 | 57 | template 58 | HFSM2_CONSTEXPR(14) 59 | CPlanT::operator bool() const noexcept { 60 | HFSM2_ASSERT(_bounds.first < TASK_CAPACITY && 61 | _bounds.last < TASK_CAPACITY || 62 | _bounds.last == INVALID_LONG); 63 | 64 | return _bounds.first < TASK_CAPACITY; 65 | } 66 | 67 | //------------------------------------------------------------------------------ 68 | 69 | template 70 | HFSM2_CONSTEXPR(14) 71 | const typename CPlanT::Task& 72 | CPlanT::first() const noexcept { 73 | HFSM2_ASSERT(_bounds.first < TASK_CAPACITY); 74 | 75 | return _planData.tasks[_bounds.first]; 76 | } 77 | 78 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 79 | 80 | template 81 | HFSM2_CONSTEXPR(14) 82 | const typename CPlanT::Task& 83 | CPlanT::last() const noexcept { 84 | HFSM2_ASSERT(_bounds.last < TASK_CAPACITY); 85 | 86 | return _planData.tasks[_bounds.last]; 87 | } 88 | 89 | //////////////////////////////////////////////////////////////////////////////// 90 | 91 | } 92 | } 93 | 94 | #endif 95 | -------------------------------------------------------------------------------- /test/test_manual_activation.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_VERBOSE_DEBUG_LOG 5 | #include "tools.hpp" 6 | 7 | using namespace test_tools; 8 | 9 | namespace test_manual_activation { 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | using Config = hfsm2::Config 14 | ::ManualActivation; 15 | 16 | using M = hfsm2::MachineT; 17 | 18 | using Logger = LoggerT; 19 | 20 | //------------------------------------------------------------------------------ 21 | 22 | #define S(s) struct s 23 | 24 | using FSM = M::PeerRoot< 25 | S(A), 26 | S(B) 27 | >; 28 | 29 | #undef S 30 | 31 | //------------------------------------------------------------------------------ 32 | 33 | static_assert(FSM::stateId() == 1, ""); 34 | static_assert(FSM::stateId() == 2, ""); 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | 38 | struct A 39 | : FSM::State 40 | { 41 | void entryGuard(GuardControl& control) { 42 | control.changeTo(); 43 | } 44 | }; 45 | 46 | //------------------------------------------------------------------------------ 47 | 48 | struct B 49 | : FSM::State 50 | {}; 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | 54 | TEST_CASE("FSM.Manual Activation") { 55 | Logger logger; 56 | 57 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 58 | 59 | WHEN("Starting") { 60 | FSM::Instance machine{&logger}; 61 | 62 | logger.assertSequence({}); 63 | assertActive(machine, {}); 64 | 65 | machine.enter(); 66 | 67 | logger.assertSequence({ 68 | { hfsm2::ROOT_ID , Event::Type::ENTRY_GUARD }, 69 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 70 | 71 | { FSM::stateId(), Event::Type::CHANGE, FSM::stateId() }, 72 | 73 | { hfsm2::ROOT_ID, Event::Type::ENTRY_GUARD }, 74 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 75 | 76 | { hfsm2::ROOT_ID, Event::Type::ENTER }, 77 | { FSM::stateId(), Event::Type::ENTER }, 78 | }); 79 | 80 | assertActive(machine, { 81 | hfsm2::ROOT_ID , 82 | FSM::stateId(), 83 | }); 84 | 85 | machine.exit(); 86 | 87 | logger.assertSequence({ 88 | { FSM::stateId(), Event::Type::EXIT }, 89 | { hfsm2::ROOT_ID , Event::Type::EXIT }, 90 | }); 91 | 92 | assertActive(machine, {}); 93 | } 94 | 95 | logger.assertSequence({}); 96 | } 97 | 98 | //////////////////////////////////////////////////////////////////////////////// 99 | 100 | } 101 | -------------------------------------------------------------------------------- /development/hfsm2/detail/shared/iterator.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | class IteratorT { 8 | public: 9 | using Container = TContainer; 10 | using Item = typename Container::Item; 11 | using Index = typename Container::Index; 12 | 13 | template 14 | friend class DynamicArrayT; 15 | 16 | private: 17 | HFSM2_CONSTEXPR(11) IteratorT(Container& container, 18 | const Index cursor) noexcept 19 | : _container{container} 20 | , _cursor{cursor} 21 | {} 22 | 23 | public: 24 | HFSM2_CONSTEXPR(14) bool operator != (const IteratorT& HFSM2_IF_ASSERT(other)) const noexcept { 25 | HFSM2_ASSERT(&_container == &other._container); 26 | 27 | return _cursor != _container.limit(); 28 | } 29 | 30 | HFSM2_CONSTEXPR(14) IteratorT& operator ++() noexcept { 31 | _cursor = _container.next(_cursor); 32 | 33 | return *this; 34 | } 35 | 36 | HFSM2_CONSTEXPR(14) Item& operator *() noexcept { return _container[_cursor]; } 37 | HFSM2_CONSTEXPR(11) const Item& operator *() const noexcept { return _container[_cursor]; } 38 | 39 | HFSM2_CONSTEXPR(14) Item* operator->() noexcept { return &_container[_cursor]; } 40 | HFSM2_CONSTEXPR(11) const Item* operator->() const noexcept { return &_container[_cursor]; } 41 | 42 | private: 43 | Container& _container; 44 | 45 | Index _cursor; 46 | }; 47 | 48 | //------------------------------------------------------------------------------ 49 | 50 | template 51 | class IteratorT { 52 | public: 53 | using Container = TContainer; 54 | using Item = typename Container::Item; 55 | using Index = typename Container::Index; 56 | 57 | template 58 | friend class DynamicArrayT; 59 | 60 | private: 61 | HFSM2_CONSTEXPR(11) IteratorT(const Container& container, 62 | const Index cursor) noexcept 63 | : _container{container} 64 | , _cursor{cursor} 65 | {} 66 | 67 | public: 68 | HFSM2_CONSTEXPR(14) bool operator != (const IteratorT& HFSM2_IF_ASSERT(other)) const noexcept { 69 | HFSM2_ASSERT(&_container == &other._container); 70 | 71 | return _cursor != _container.limit(); 72 | } 73 | 74 | HFSM2_CONSTEXPR(14) IteratorT& operator ++() noexcept { 75 | _cursor = _container.next(_cursor); 76 | 77 | return *this; 78 | } 79 | 80 | HFSM2_CONSTEXPR(11) const Item& operator *() const noexcept { return _container[_cursor]; } 81 | 82 | HFSM2_CONSTEXPR(11) const Item* operator->() const noexcept { return &operator *(); } 83 | 84 | private: 85 | const Container& _container; 86 | 87 | Index _cursor; 88 | }; 89 | 90 | //////////////////////////////////////////////////////////////////////////////// 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /development/hfsm2/detail/structure/ancestors_1.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | struct A_; 8 | 9 | template 10 | using EmptyT = A_>; 11 | 12 | //------------------------------------------------------------------------------ 13 | 14 | template 15 | struct HFSM2_EMPTY_BASES A_ 16 | : TFirst 17 | , A_ 18 | { 19 | using First = TFirst; 20 | using typename First::Context; 21 | 22 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 23 | using typename First::Rank; 24 | using typename First::Utility; 25 | #endif 26 | 27 | using typename First::StateList; 28 | using typename First::RegionList; 29 | 30 | using typename First::ConstControl; 31 | using typename First::Control; 32 | using typename First::PlanControl; 33 | 34 | #if HFSM2_PLANS_AVAILABLE() 35 | using typename First::Plan; 36 | #endif 37 | 38 | using typename First::FullControl; 39 | using typename First::GuardControl; 40 | using typename First::EventControl; 41 | 42 | using First::stateId; 43 | using First::regionId; 44 | 45 | using Rest = A_; 46 | 47 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 48 | 49 | HFSM2_CONSTEXPR(14) void wideEntryGuard(GuardControl& control) noexcept; 50 | 51 | HFSM2_CONSTEXPR(14) void wideEnter ( PlanControl& control) noexcept; 52 | HFSM2_CONSTEXPR(14) void wideReenter ( PlanControl& control) noexcept; 53 | 54 | HFSM2_CONSTEXPR(14) void widePreUpdate ( FullControl& control) noexcept; 55 | HFSM2_CONSTEXPR(14) void wideUpdate ( FullControl& control) noexcept; 56 | HFSM2_CONSTEXPR(14) void widePostUpdate( FullControl& control) noexcept; 57 | 58 | template 59 | HFSM2_CONSTEXPR(14) void widePreReact (const TEvent& event , 60 | EventControl& control) noexcept; 61 | 62 | template 63 | HFSM2_CONSTEXPR(14) void wideReact (const TEvent& event , 64 | EventControl& control) noexcept; 65 | 66 | template 67 | HFSM2_CONSTEXPR(14) void widePostReact (const TEvent& event , 68 | EventControl& control) noexcept; 69 | 70 | template 71 | HFSM2_CONSTEXPR(14) void wideQuery ( TEvent& event , 72 | ConstControl& control) const noexcept; 73 | 74 | HFSM2_CONSTEXPR(14) void wideExitGuard (GuardControl& control) noexcept; 75 | 76 | HFSM2_CONSTEXPR(14) void wideExit ( PlanControl& control) noexcept; 77 | 78 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 79 | }; 80 | 81 | //////////////////////////////////////////////////////////////////////////////// 82 | 83 | } 84 | } 85 | 86 | #include "ancestors_1.inl" 87 | -------------------------------------------------------------------------------- /development/hfsm2/detail/structure/base.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | class B_ { 8 | template 9 | friend struct A_; 10 | 11 | protected: 12 | using Context = typename TArgs::Context; 13 | 14 | using Short = ::hfsm2::Short; 15 | using Prong = ::hfsm2::Prong; 16 | 17 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 18 | using Rank = typename TArgs::Rank; 19 | using Utility = typename TArgs::Utility; 20 | #endif 21 | 22 | using StateList = typename TArgs::StateList; 23 | using RegionList = typename TArgs::RegionList; 24 | 25 | using Payload = typename TArgs::Payload; 26 | using Transition = TransitionT; 27 | 28 | using ConstControl = ConstControlT; 29 | using Control = ControlT ; 30 | using PlanControl = PlanControlT ; 31 | using FullControl = FullControlT ; 32 | using GuardControl = GuardControlT; 33 | using EventControl = EventControlT; 34 | 35 | #if HFSM2_PLANS_AVAILABLE() 36 | using Plan = PayloadPlanT ; 37 | #endif 38 | 39 | public: 40 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 41 | 42 | HFSM2_CONSTEXPR(14) void entryGuard (GuardControl& ) noexcept {} 43 | 44 | HFSM2_CONSTEXPR(14) void enter ( PlanControl& ) noexcept {} 45 | HFSM2_CONSTEXPR(14) void reenter ( PlanControl& ) noexcept {} 46 | 47 | HFSM2_CONSTEXPR(14) void preUpdate ( FullControl& ) noexcept {} 48 | HFSM2_CONSTEXPR(14) void update ( FullControl& ) noexcept {} 49 | HFSM2_CONSTEXPR(14) void postUpdate ( FullControl& ) noexcept {} 50 | 51 | template 52 | HFSM2_CONSTEXPR(14) void preReact (const TEvent& , 53 | EventControl& ) noexcept {} 54 | 55 | template 56 | HFSM2_CONSTEXPR(14) void react (const TEvent& , 57 | EventControl& ) noexcept {} 58 | 59 | template 60 | HFSM2_CONSTEXPR(14) void postReact (const TEvent& , 61 | EventControl& ) noexcept {} 62 | 63 | template 64 | HFSM2_CONSTEXPR(14) void query ( TEvent& , 65 | ConstControl& ) const noexcept {} 66 | 67 | HFSM2_CONSTEXPR(14) void exitGuard (GuardControl& ) noexcept {} 68 | 69 | HFSM2_CONSTEXPR(14) void exit ( PlanControl& ) noexcept {} 70 | 71 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 72 | 73 | template 74 | static 75 | HFSM2_CONSTEXPR(11) StateID stateId() noexcept { return index() ; } 76 | 77 | template 78 | static 79 | HFSM2_CONSTEXPR(11) RegionID regionId() noexcept { return static_cast(index()); } 80 | }; 81 | 82 | //////////////////////////////////////////////////////////////////////////////// 83 | 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /development/hfsm2/detail/shared/bit_stream.hpp: -------------------------------------------------------------------------------- 1 | #if HFSM2_SERIALIZATION_AVAILABLE() 2 | 3 | namespace hfsm2 { 4 | namespace detail { 5 | 6 | //------------------------------------------------------------------------------ 7 | 8 | template 9 | class BitWriteStreamT; 10 | 11 | template 12 | class BitReadStreamT; 13 | 14 | //////////////////////////////////////////////////////////////////////////////// 15 | 16 | template 17 | class StreamBufferT { 18 | template 19 | friend class BitWriteStreamT; 20 | 21 | template 22 | friend class BitReadStreamT; 23 | 24 | public: 25 | static constexpr Long BIT_CAPACITY = NBitCapacity; 26 | static constexpr Long BYTE_COUNT = contain(BIT_CAPACITY, 8u); 27 | 28 | using StreamBuffer = StreamBufferT; 29 | using Data = uint8_t [BYTE_COUNT ]; 30 | 31 | HFSM2_CONSTEXPR(14) void clear() noexcept { fill(_data, 0); } 32 | 33 | HFSM2_CONSTEXPR(14) Data& data() noexcept { return _data; } 34 | HFSM2_CONSTEXPR(11) const Data& data() const noexcept { return _data; } 35 | 36 | HFSM2_CONSTEXPR(14) bool operator == (const StreamBuffer& s) const noexcept; 37 | HFSM2_CONSTEXPR(14) bool operator != (const StreamBuffer& s) const noexcept; 38 | 39 | private: 40 | Data _data {}; 41 | }; 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | 45 | template 46 | class BitWriteStreamT final { 47 | public: 48 | static constexpr Long BIT_CAPACITY = NBitCapacity; 49 | 50 | using Buffer = StreamBufferT; 51 | 52 | public: 53 | HFSM2_CONSTEXPR(14) explicit BitWriteStreamT(Buffer& buffer, 54 | const Long cursor = 0) noexcept 55 | : _buffer{buffer} 56 | , _cursor{cursor} 57 | { 58 | _buffer.clear(); 59 | } 60 | 61 | template 62 | HFSM2_CONSTEXPR(14) void write(const UBitWidth item) noexcept; 63 | 64 | HFSM2_CONSTEXPR(11) Long cursor() const noexcept { return _cursor; } 65 | 66 | private: 67 | Buffer& _buffer; 68 | 69 | Long _cursor = 0; 70 | }; 71 | 72 | //------------------------------------------------------------------------------ 73 | 74 | template 75 | class BitReadStreamT final { 76 | public: 77 | static constexpr Long BIT_CAPACITY = NBitCapacity; 78 | 79 | using Buffer = StreamBufferT; 80 | 81 | public: 82 | HFSM2_CONSTEXPR(11) explicit BitReadStreamT(const Buffer& buffer, 83 | const Long cursor = 0) noexcept 84 | : _buffer{buffer} 85 | , _cursor{cursor} 86 | {} 87 | 88 | template 89 | HFSM2_CONSTEXPR(14) UBitWidth read() noexcept; 90 | 91 | HFSM2_CONSTEXPR(11) Long cursor() const noexcept { return _cursor; } 92 | 93 | private: 94 | const Buffer& _buffer; 95 | 96 | Long _cursor; 97 | }; 98 | 99 | //////////////////////////////////////////////////////////////////////////////// 100 | 101 | } 102 | } 103 | 104 | #include "bit_stream.inl" 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /development/hfsm2/detail/features/task.hpp: -------------------------------------------------------------------------------- 1 | #if HFSM2_PLANS_AVAILABLE() 2 | 3 | namespace hfsm2 { 4 | namespace detail { 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | struct TaskBase { 9 | static_assert(sizeof(Long) == sizeof(StateID), ""); 10 | 11 | HFSM2_CONSTEXPR(11) TaskBase() noexcept {} 12 | 13 | HFSM2_CONSTEXPR(11) TaskBase(const StateID origin_, 14 | const StateID destination_, 15 | const TransitionType type_) noexcept 16 | : origin{origin_} 17 | , destination{destination_} 18 | , type{type_} 19 | {} 20 | 21 | HFSM2_CONSTEXPR(11) bool cyclic() const noexcept { return origin == destination; } 22 | 23 | union { 24 | StateID origin = INVALID_STATE_ID; 25 | Long prev; 26 | }; 27 | 28 | union { 29 | StateID destination = INVALID_STATE_ID; 30 | Long next; 31 | }; 32 | 33 | TransitionType type = TransitionType::COUNT; 34 | }; 35 | 36 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 37 | 38 | HFSM2_CONSTEXPR(11) 39 | bool 40 | operator == (const TaskBase& lhs, 41 | const TaskBase& rhs) noexcept 42 | { 43 | return lhs.origin == rhs.origin && 44 | lhs.destination == rhs.destination && 45 | lhs.type == rhs.type; 46 | } 47 | 48 | //------------------------------------------------------------------------------ 49 | 50 | template 51 | struct TaskT final 52 | : TaskBase 53 | { 54 | using Payload = TPayload; 55 | using Storage = uint8_t[sizeof(Payload)]; 56 | 57 | using TaskBase::TaskBase; 58 | 59 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 60 | 61 | HFSM2_CONSTEXPR(14) TaskT() noexcept { 62 | new (&storage) Payload{}; 63 | } 64 | 65 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 66 | 67 | HFSM2_CONSTEXPR(14) TaskT(const StateID origin_, 68 | const StateID destination_, 69 | const TransitionType type_, 70 | const Payload& payload) noexcept 71 | : TaskBase{origin_, destination_, type_} 72 | , payloadSet{true} 73 | { 74 | new (&storage) Payload{payload}; 75 | } 76 | 77 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 78 | 79 | HFSM2_CONSTEXPR(11) 80 | const Payload* 81 | payload() const noexcept { 82 | return payloadSet ? 83 | reinterpret_cast(&storage) : nullptr; 84 | } 85 | 86 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 87 | 88 | #ifdef _MSC_VER 89 | #pragma warning(push) 90 | #pragma warning(disable: 4324) // structure was padded due to alignment specifier 91 | #endif 92 | 93 | alignas(Payload) Storage storage {}; 94 | 95 | #ifdef _MSC_VER 96 | #pragma warning(pop) 97 | #endif 98 | 99 | bool payloadSet = false; 100 | }; 101 | 102 | //------------------------------------------------------------------------------ 103 | 104 | template <> 105 | struct TaskT final 106 | : TaskBase 107 | { 108 | using TaskBase::TaskBase; 109 | }; 110 | 111 | //////////////////////////////////////////////////////////////////////////////// 112 | 113 | } 114 | } 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /test/reported/issue_49.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | // Issue reported in https://github.com/andrew-gresyk/HFSM2/issues/49 5 | 6 | #define HFSM2_ENABLE_PLANS 7 | #include 8 | 9 | #include 10 | 11 | namespace issue_49 { 12 | 13 | using Config = hfsm2::Config 14 | ::TaskCapacityN<3>; 15 | 16 | using M = hfsm2::MachineT; 17 | 18 | using FSM = M::PeerRoot< 19 | struct Disabled, 20 | M::Composite, 27 | struct Idle 28 | > 29 | >; 30 | 31 | // Events 32 | struct Enable {}; 33 | struct Disable {}; 34 | 35 | struct Disabled : FSM::State 36 | { 37 | void update(FullControl& control) 38 | { 39 | control.changeTo(); 40 | } 41 | 42 | void react(const Enable&, EventControl& control) 43 | { 44 | control.changeTo(); 45 | } 46 | 47 | using FSM::State::react; 48 | }; 49 | 50 | struct Enabled : FSM::State 51 | { 52 | void react(const Disable&, EventControl& control) 53 | { 54 | control.changeTo(); 55 | } 56 | 57 | using FSM::State::react; 58 | }; 59 | 60 | struct Active : FSM::State 61 | { 62 | void planSucceeded(FullControl& control) 63 | { 64 | control.changeTo(); 65 | } 66 | }; 67 | 68 | struct Idle : FSM::State 69 | { 70 | void update(FullControl& control) 71 | { 72 | control.changeTo(); 73 | } 74 | }; 75 | 76 | struct A : FSM::State 77 | { 78 | void enter(PlanControl& control) 79 | { 80 | auto plan = control.plan(); 81 | plan.clear(); 82 | 83 | plan.change(); 84 | plan.change(); 85 | plan.change(); 86 | } 87 | 88 | void update(FullControl& control) { control.succeed(); } 89 | }; 90 | 91 | struct B : FSM::State 92 | { 93 | void update(FullControl& control) { control.succeed(); } 94 | }; 95 | 96 | struct C : FSM::State 97 | { 98 | void update(FullControl& control) { control.succeed(); } 99 | }; 100 | 101 | struct D : FSM::State 102 | { 103 | void update(FullControl& control) { control.succeed(); } 104 | }; 105 | 106 | TEST_CASE("FSM.Reported.Issue_49") { 107 | FSM::Instance fsm; 108 | 109 | REQUIRE(fsm.isActive()); 110 | 111 | fsm.update(); 112 | REQUIRE(fsm.isActive()); 113 | REQUIRE(fsm.isActive()); 114 | REQUIRE(fsm.isActive()); 115 | 116 | fsm.update(); 117 | REQUIRE(fsm.isActive()); 118 | 119 | fsm.update(); 120 | REQUIRE(fsm.isActive()); 121 | 122 | fsm.update(); 123 | REQUIRE(fsm.isActive()); 124 | 125 | fsm.update(); 126 | REQUIRE(fsm.isActive()); 127 | 128 | fsm.update(); 129 | REQUIRE(fsm.isActive()); 130 | 131 | fsm.update(); 132 | REQUIRE(fsm.isActive()); 133 | 134 | // uncomment to make test pass 135 | //fsm.update(); 136 | //REQUIRE(fsm.isActive()); 137 | 138 | fsm.react(Disable{}); 139 | REQUIRE(fsm.isActive()); 140 | 141 | fsm.react(Enable{}); 142 | REQUIRE(fsm.isActive()); 143 | REQUIRE(fsm.isActive()); 144 | REQUIRE(fsm.isActive()); 145 | 146 | fsm.update(); 147 | REQUIRE(fsm.isActive()); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /test/reported/resuming_plan.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | // Issue reported in https://discord.com/channels/755015945269018695/759099438353350696/1334175930188300348 5 | 6 | #define HFSM2_ENABLE_PLANS 7 | #include 8 | 9 | #include 10 | 11 | using M = hfsm2::Machine; 12 | 13 | struct Manual; 14 | struct Automatic; 15 | struct Idling; 16 | struct WaitInput; 17 | struct WorkOnBox; 18 | struct BoxA; 19 | struct BoxB; 20 | struct BoxC; 21 | 22 | enum class Input { retry, stop }; 23 | 24 | struct BoxPresent { }; 25 | 26 | // clang-format off 27 | using FSM = M::PeerRoot< 28 | Manual, 29 | M::Composite 37 | > 38 | >; 39 | // clang-format on 40 | 41 | struct Manual : FSM::State { }; 42 | 43 | struct Automatic : FSM::State { }; 44 | 45 | struct Idling : FSM::State { 46 | using FSM::State::react; 47 | 48 | void react(const BoxPresent&, EventControl& c) 49 | { 50 | auto plan = c.plan(); 51 | 52 | plan.change(); 53 | plan.change(); 54 | 55 | c.changeTo(); 56 | } 57 | }; 58 | 59 | struct WaitInput : FSM::State { 60 | using FSM::State::react; 61 | 62 | void react(const Input& e, EventControl& c) 63 | { 64 | if (e == Input::retry) 65 | c.resume(); 66 | else 67 | c.changeTo(); 68 | } 69 | }; 70 | 71 | struct WorkOnBox : FSM::State { 72 | void planSucceeded(FullControl& c) 73 | { 74 | c.changeTo(); 75 | } 76 | 77 | void planFailed(FullControl& c) 78 | { 79 | c.changeTo(); 80 | } 81 | }; 82 | 83 | struct BoxA : FSM::State { 84 | void update(FullControl& c) 85 | { 86 | c.succeed(); 87 | } 88 | }; 89 | 90 | struct BoxB : FSM::State { 91 | bool should_fail = true; 92 | 93 | void update(FullControl& c) 94 | { 95 | if (should_fail) 96 | c.fail(); 97 | else 98 | c.succeed(); 99 | 100 | should_fail = !should_fail; 101 | } 102 | }; 103 | 104 | struct BoxC : FSM::State { 105 | void update(FullControl& c) 106 | { 107 | c.succeed(); 108 | } 109 | }; 110 | 111 | TEST_CASE("FSM.Reported.Resuming Plan") { 112 | FSM::Instance machine; 113 | 114 | machine.immediateChangeTo(); 115 | REQUIRE(machine.isActive()); 116 | REQUIRE(machine.isActive()); 117 | 118 | machine.react(BoxPresent {}); 119 | REQUIRE(machine.isActive()); 120 | REQUIRE(machine.isActive()); 121 | REQUIRE(machine.isActive()); 122 | 123 | machine.update(); 124 | REQUIRE(machine.isActive()); 125 | REQUIRE(machine.isActive()); 126 | REQUIRE(machine.isActive()); 127 | 128 | machine.update(); 129 | REQUIRE(machine.isActive()); 130 | REQUIRE(machine.isActive()); 131 | 132 | machine.react(Input::retry); 133 | REQUIRE(machine.isActive()); 134 | REQUIRE(machine.isActive()); 135 | REQUIRE(machine.isActive()); 136 | 137 | machine.update(); 138 | REQUIRE(machine.isActive()); 139 | REQUIRE(machine.isActive()); 140 | REQUIRE(machine.isActive()); 141 | } 142 | -------------------------------------------------------------------------------- /development/hfsm2/detail/shared/bit_stream.inl: -------------------------------------------------------------------------------- 1 | #if HFSM2_SERIALIZATION_AVAILABLE() 2 | 3 | namespace hfsm2 { 4 | namespace detail { 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | template 9 | HFSM2_CONSTEXPR(14) 10 | bool 11 | StreamBufferT::operator == (const StreamBuffer& buffer) const noexcept { 12 | for (Long i = 0; i < BYTE_COUNT; ++i) 13 | if (_data[i] != buffer._data[i]) 14 | return false; 15 | 16 | return true; 17 | } 18 | 19 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 20 | 21 | template 22 | HFSM2_CONSTEXPR(14) 23 | bool 24 | StreamBufferT::operator != (const StreamBuffer& buffer) const noexcept { 25 | for (Long i = 0; i < BYTE_COUNT; ++i) 26 | if (_data[i] != buffer._data[i]) 27 | return true; 28 | 29 | return false; 30 | } 31 | 32 | //////////////////////////////////////////////////////////////////////////////// 33 | 34 | template 35 | template 36 | HFSM2_CONSTEXPR(14) 37 | void 38 | BitWriteStreamT::write(const UBitWidth item) noexcept { 39 | constexpr Short BIT_WIDTH = NBitWidth; 40 | static_assert(BIT_WIDTH > 0, "STATIC ASSERT"); 41 | 42 | HFSM2_ASSERT(_cursor + BIT_WIDTH <= BIT_CAPACITY); 43 | 44 | using Item = UBitWidth; 45 | 46 | Item itemBits = item; 47 | 48 | for (Short itemWidth = BIT_WIDTH; itemWidth; ) { 49 | const Long byteIndex = _cursor >> 3; 50 | uint8_t& byte = _buffer._data[byteIndex]; 51 | 52 | const Short byteChunkStart = _cursor & 0x7; 53 | const Short byteDataWidth = 8 - byteChunkStart; 54 | const Short byteChunkWidth = min(byteDataWidth, itemWidth); 55 | const Item byteChunk = itemBits << byteChunkStart; 56 | 57 | byte |= byteChunk; 58 | itemBits >>= byteChunkWidth; 59 | itemWidth -= byteChunkWidth; 60 | _cursor += byteChunkWidth; 61 | } 62 | } 63 | 64 | //////////////////////////////////////////////////////////////////////////////// 65 | 66 | template 67 | template 68 | HFSM2_CONSTEXPR(14) 69 | UBitWidth 70 | BitReadStreamT::read() noexcept { 71 | constexpr Short BIT_WIDTH = NBitWidth; 72 | static_assert(BIT_WIDTH > 0, "STATIC ASSERT"); 73 | 74 | HFSM2_ASSERT(_cursor + BIT_WIDTH <= BIT_CAPACITY); 75 | 76 | using Item = UBitWidth; 77 | 78 | Item item = 0; 79 | 80 | for (Short itemCursor = 0, itemWidth = BIT_WIDTH; itemWidth; ) { 81 | const Long byteIndex = _cursor >> 3; 82 | const uint8_t& byte = _buffer._data[byteIndex]; 83 | 84 | const Short byteChunkStart = _cursor & 0x7; 85 | const Short byteDataWidth = 8 - byteChunkStart; 86 | const Short byteChunkWidth = min(byteDataWidth, itemWidth); 87 | const Short byteChunkMask = (1 << byteChunkWidth) - 1; 88 | 89 | const Item byteChunk = (byte >> byteChunkStart) & byteChunkMask; 90 | const Item itemChunk = byteChunk << itemCursor; 91 | 92 | item |= itemChunk; 93 | itemCursor += byteChunkWidth; 94 | itemWidth -= byteChunkWidth; 95 | _cursor += byteChunkWidth; 96 | } 97 | 98 | return item; 99 | } 100 | 101 | //////////////////////////////////////////////////////////////////////////////// 102 | 103 | } 104 | } 105 | 106 | #endif 107 | -------------------------------------------------------------------------------- /development/hfsm2/detail/features/logger_interface.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | 3 | //------------------------------------------------------------------------------ 4 | 5 | struct HFSM2_EMPTY_BASES EmptyContext {}; 6 | 7 | #if HFSM2_LOG_INTERFACE_AVAILABLE() 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | template < 12 | FeatureTag NFeatureTag = HFSM2_FEATURE_TAG 13 | , typename TContext = EmptyContext 14 | HFSM2_IF_UTILITY_THEORY(, typename TUtilty = float) 15 | > 16 | struct LoggerInterfaceT { 17 | using Context = TContext; 18 | 19 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 20 | using Utilty = TUtilty; 21 | #endif 22 | 23 | using Method = ::hfsm2::Method; 24 | using Prong = ::hfsm2::Prong; 25 | using StateID = ::hfsm2::StateID; 26 | using TransitionType = ::hfsm2::TransitionType; 27 | 28 | #if HFSM2_PLANS_AVAILABLE() 29 | using StatusEvent = ::hfsm2::StatusEvent; 30 | #endif 31 | 32 | HFSM2_CONSTEXPR(NO) 33 | virtual 34 | void 35 | recordMethod(const Context& HFSM2_UNUSED(context), 36 | const StateID HFSM2_UNUSED(origin), 37 | const Method HFSM2_UNUSED(method)) 38 | {} 39 | 40 | HFSM2_CONSTEXPR(NO) 41 | virtual 42 | void 43 | recordTransition(const Context& HFSM2_UNUSED(context), 44 | const StateID HFSM2_UNUSED(origin), 45 | const TransitionType HFSM2_UNUSED(transitionType), 46 | const StateID HFSM2_UNUSED(target)) 47 | {} 48 | 49 | #if HFSM2_PLANS_AVAILABLE() 50 | 51 | HFSM2_CONSTEXPR(NO) 52 | virtual 53 | void 54 | recordTaskStatus(const Context& HFSM2_UNUSED(context), 55 | const StateID HFSM2_UNUSED(region), 56 | const StateID HFSM2_UNUSED(origin), 57 | const StatusEvent HFSM2_UNUSED(event)) 58 | {} 59 | 60 | HFSM2_CONSTEXPR(NO) 61 | virtual 62 | void 63 | recordPlanStatus(const Context& HFSM2_UNUSED(context), 64 | const StateID HFSM2_UNUSED(region), 65 | const StatusEvent HFSM2_UNUSED(event)) 66 | {} 67 | 68 | #endif 69 | 70 | HFSM2_CONSTEXPR(NO) 71 | virtual 72 | void 73 | recordCancelledPending(const Context& HFSM2_UNUSED(context), 74 | const StateID HFSM2_UNUSED(origin)) 75 | {} 76 | 77 | HFSM2_CONSTEXPR(NO) 78 | virtual 79 | void 80 | recordSelectResolution(const Context& HFSM2_UNUSED(context), 81 | const StateID HFSM2_UNUSED(head), 82 | const Prong HFSM2_UNUSED(prong)) 83 | {} 84 | 85 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 86 | 87 | HFSM2_CONSTEXPR(NO) 88 | virtual 89 | void 90 | recordUtilityResolution(const Context& HFSM2_UNUSED(context), 91 | const StateID HFSM2_UNUSED(head), 92 | const Prong HFSM2_UNUSED(prong), 93 | const Utilty HFSM2_UNUSED(utilty)) 94 | {} 95 | 96 | HFSM2_CONSTEXPR(NO) 97 | virtual 98 | void 99 | recordRandomResolution(const Context& HFSM2_UNUSED(context), 100 | const StateID HFSM2_UNUSED(head), 101 | const Prong HFSM2_UNUSED(prong), 102 | const Utilty HFSM2_UNUSED(utilty)) 103 | {} 104 | 105 | #endif 106 | }; 107 | 108 | //////////////////////////////////////////////////////////////////////////////// 109 | 110 | #else 111 | 112 | template < 113 | FeatureTag NFeatureTag = HFSM2_FEATURE_TAG 114 | , typename TContext = EmptyContext 115 | HFSM2_IF_UTILITY_THEORY(, typename TUtilty = float) 116 | > 117 | using LoggerInterfaceT = void; 118 | 119 | #endif 120 | 121 | using LoggerInterface = LoggerInterfaceT<>; 122 | 123 | } 124 | -------------------------------------------------------------------------------- /CMake/modules/coverage.cmake: -------------------------------------------------------------------------------- 1 | if (BUILD_COVERAGE) 2 | set(CMAKE_BUILD_TYPE "Debug") 3 | if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") 4 | find_program(GCOV_PATH gcov) 5 | find_program(GENHTML_PATH genhtml) 6 | find_program(LCOV_PATH lcov) 7 | 8 | if(NOT LCOV_PATH) 9 | message(FATAL_ERROR "lcov not found.") 10 | endif(NOT LCOV_PATH) 11 | 12 | if(NOT GCOV_PATH) 13 | message(FATAL_ERROR "gcov not found.") 14 | endif(NOT GCOV_PATH) 15 | 16 | if(NOT GENHTML_PATH) 17 | message(FATAL_ERROR "GENHTML not found.") 18 | endif(NOT GENHTML_PATH) 19 | 20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-arcs -ftest-coverage -U_FORTIFY_SOURCE") 21 | 22 | add_custom_target(coverage ALL 23 | COMMAND ${LCOV_PATH} -z -d ${CMAKE_CURRENT_BINARY_DIR} 24 | COMMAND ${LCOV_PATH} --no-external -c -i -d ${CMAKE_SOURCE_DIR} -o ${TEST_PROJECT}_base.info 25 | COMMAND ${TEST_PROJECT} 26 | COMMAND ${LCOV_PATH} --no-external -c -d ${CMAKE_SOURCE_DIR} -o ${TEST_PROJECT}_test.info 27 | COMMAND ${LCOV_PATH} -a ${TEST_PROJECT}_base.info -a ${TEST_PROJECT}_test.info -o ${TEST_PROJECT}_total.info 28 | COMMAND ${LCOV_PATH} --remove ${TEST_PROJECT}_total.info '${PROJECT_BINARY_DIR}/*' -o ${TEST_PROJECT}.profdata 29 | COMMAND ${GENHTML_PATH} -o ${TEST_PROJECT}-coverage ${TEST_PROJECT}.profdata 30 | DEPENDS ${TEST_PROJECT} 31 | BYPRODUCTS ${TEST_PROJECT}_base.info ${TEST_PROJECT}_test.info ${TEST_PROJECT}_total.info ${TEST_PROJECT}.profdata 32 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 33 | ) 34 | elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") 35 | find_program(LCOV_PATH llvm-cov) 36 | 37 | if(NOT LCOV_PATH) 38 | message(FATAL_ERROR "llvm-cov not found.") 39 | endif(NOT LCOV_PATH) 40 | 41 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O0 -fprofile-instr-generate -fcoverage-mapping") 42 | 43 | file (GLOB SOURCES "${PROJECT_SOURCE_DIR}/*.hpp" 44 | "${PROJECT_SOURCE_DIR}/*.cpp" 45 | "${PROJECT_SOURCE_DIR}/test/*.hpp" 46 | "${PROJECT_SOURCE_DIR}/test/*.cpp" 47 | ) 48 | 49 | # llvm-cov 50 | add_custom_target(${TEST_PROJECT}-ccov-preprocessing 51 | COMMAND LLVM_PROFILE_FILE=${TEST_PROJECT}.profraw $ 52 | COMMAND llvm-profdata merge -sparse ${TEST_PROJECT}.profraw -o ${TEST_PROJECT}.profdata 53 | DEPENDS ${TEST_PROJECT} 54 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 55 | 56 | add_custom_target(${TEST_PROJECT}-ccov-show 57 | COMMAND llvm-cov show $ -instr-profile=${TEST_PROJECT}.profdata -show-line-counts-or-regions 58 | DEPENDS ${TEST_PROJECT}-ccov-preprocessing 59 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 60 | 61 | add_custom_target(${TEST_PROJECT}-ccov-report 62 | COMMAND llvm-cov report $ -instr-profile=${TEST_PROJECT}.profdata ${SOURCES} 63 | DEPENDS ${TEST_PROJECT}-ccov-preprocessing 64 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 65 | 66 | add_custom_target(coverage ALL 67 | COMMAND llvm-cov show $ -instr-profile=${TEST_PROJECT}.profdata -show-line-counts-or-regions -output-dir=${CMAKE_CURRENT_BINARY_DIR}/${TEST_PROJECT}-coverage -format="html" 68 | DEPENDS ${TEST_PROJECT}-ccov-report 69 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) 70 | endif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") 71 | endif(BUILD_COVERAGE) 72 | -------------------------------------------------------------------------------- /test/shared/test_bit_array.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #include "../tools.hpp" 5 | 6 | namespace test_bit_array { 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | using BitArray = hfsm2::detail::BitArrayT<32>; 11 | using Bits = typename BitArray::Bits; 12 | 13 | TEST_CASE("Shared.BitArrayT<>") { 14 | BitArray bitArray; 15 | REQUIRE(bitArray.empty()); 16 | 17 | WHEN("Static methods") { 18 | { 19 | const Bits bits = bitArray.bits<3, 7>(); 20 | REQUIRE(!bits); 21 | 22 | REQUIRE(!bits.get<0>()); 23 | REQUIRE(!bits.get<1>()); 24 | REQUIRE(!bits.get<2>()); 25 | REQUIRE(!bits.get<3>()); 26 | REQUIRE(!bits.get<4>()); 27 | REQUIRE(!bits.get<5>()); 28 | REQUIRE(!bits.get<6>()); 29 | } 30 | 31 | { 32 | Bits bits = bitArray.bits<3, 7>(); 33 | bits.set<4>(); 34 | } 35 | 36 | { 37 | const Bits bits = bitArray.bits<2, 13>(); 38 | REQUIRE(!bits.get< 0>()); 39 | REQUIRE(!bits.get< 1>()); 40 | REQUIRE(!bits.get< 2>()); 41 | REQUIRE(!bits.get< 3>()); 42 | REQUIRE(!bits.get< 4>()); 43 | REQUIRE(!bits.get< 5>()); 44 | REQUIRE(!bits.get< 6>()); 45 | REQUIRE(!bits.get< 7>()); 46 | REQUIRE(!bits.get< 8>()); 47 | REQUIRE(!bits.get< 9>()); 48 | REQUIRE(!bits.get<10>()); 49 | REQUIRE(!bits.get<11>()); 50 | REQUIRE( bits.get<12>()); 51 | } 52 | 53 | { 54 | Bits bits = bitArray.bits<2, 13>(); 55 | bits.clear<12>(); 56 | } 57 | 58 | { 59 | const Bits bits = bitArray.bits<3, 7>(); 60 | REQUIRE(!bits); 61 | 62 | REQUIRE(!bits.get<0>()); 63 | REQUIRE(!bits.get<1>()); 64 | REQUIRE(!bits.get<2>()); 65 | REQUIRE(!bits.get<3>()); 66 | REQUIRE(!bits.get<4>()); 67 | REQUIRE(!bits.get<5>()); 68 | REQUIRE(!bits.get<6>()); 69 | } 70 | 71 | bitArray.clear(); 72 | REQUIRE(bitArray.empty()); 73 | } 74 | 75 | WHEN("Dynamic methods") { 76 | { 77 | const Bits bits = bitArray.bits<3, 7>(); 78 | REQUIRE(!bits); 79 | 80 | REQUIRE(!bits.get(0)); 81 | REQUIRE(!bits.get(1)); 82 | REQUIRE(!bits.get(2)); 83 | REQUIRE(!bits.get(3)); 84 | REQUIRE(!bits.get(4)); 85 | REQUIRE(!bits.get(5)); 86 | REQUIRE(!bits.get(6)); 87 | } 88 | 89 | { 90 | Bits bits = bitArray.bits<3, 7>(); 91 | bits.set<4>(); 92 | } 93 | 94 | { 95 | const Bits bits = bitArray.bits<2, 13>(); 96 | REQUIRE(!bits.get( 0)); 97 | REQUIRE(!bits.get( 1)); 98 | REQUIRE(!bits.get( 2)); 99 | REQUIRE(!bits.get( 3)); 100 | REQUIRE(!bits.get( 4)); 101 | REQUIRE(!bits.get( 5)); 102 | REQUIRE(!bits.get( 6)); 103 | REQUIRE(!bits.get( 7)); 104 | REQUIRE(!bits.get( 8)); 105 | REQUIRE(!bits.get( 9)); 106 | REQUIRE(!bits.get(10)); 107 | REQUIRE(!bits.get(11)); 108 | REQUIRE( bits.get(12)); 109 | } 110 | 111 | { 112 | Bits bits = bitArray.bits<2, 13>(); 113 | bits.clear<12>(); 114 | } 115 | 116 | { 117 | const Bits bits = bitArray.bits<3, 7>(); 118 | REQUIRE(!bits); 119 | 120 | REQUIRE(!bits.get(0)); 121 | REQUIRE(!bits.get(1)); 122 | REQUIRE(!bits.get(2)); 123 | REQUIRE(!bits.get(3)); 124 | REQUIRE(!bits.get(4)); 125 | REQUIRE(!bits.get(5)); 126 | REQUIRE(!bits.get(6)); 127 | } 128 | 129 | bitArray.clear(); 130 | REQUIRE(bitArray.empty()); 131 | } 132 | } 133 | 134 | //////////////////////////////////////////////////////////////////////////////// 135 | 136 | } 137 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/control_1.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | HFSM2_CONSTEXPR(14) 8 | ControlT::Origin::Origin(ControlT& control_, 9 | const StateID stateId_) noexcept 10 | : control{control_} 11 | , prevId{control._originId} 12 | { 13 | HFSM2_ASSERT(stateId_ < StateList::SIZE); 14 | control._originId = stateId_; 15 | } 16 | 17 | //------------------------------------------------------------------------------ 18 | 19 | template 20 | HFSM2_CONSTEXPR(20) 21 | ControlT::Origin::~Origin() noexcept { 22 | control._originId = prevId; 23 | } 24 | 25 | //////////////////////////////////////////////////////////////////////////////// 26 | 27 | template 28 | HFSM2_CONSTEXPR(14) 29 | ControlT::Region::Region(ControlT& control_, 30 | const RegionID regionId_) noexcept 31 | : control{control_} 32 | , prevId{control._regionId} 33 | { 34 | control.setRegion(regionId_); 35 | } 36 | 37 | //------------------------------------------------------------------------------ 38 | 39 | template 40 | HFSM2_CONSTEXPR(20) 41 | ControlT::Region::~Region() noexcept { 42 | control.resetRegion(prevId); 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | template 48 | HFSM2_CONSTEXPR(14) 49 | void 50 | ControlT::setRegion(const RegionID regionId_) noexcept { 51 | HFSM2_ASSERT(_regionId <= regionId_ && regionId_ < RegionList::SIZE); 52 | 53 | _regionId = regionId_; 54 | } 55 | 56 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 57 | 58 | template 59 | HFSM2_CONSTEXPR(14) 60 | void 61 | ControlT::resetRegion(const RegionID regionId_) noexcept { 62 | HFSM2_ASSERT(regionId_ <= _regionId && _regionId < RegionList::SIZE); 63 | 64 | _regionId = regionId_; 65 | } 66 | 67 | //------------------------------------------------------------------------------ 68 | 69 | #if HFSM2_TRANSITION_HISTORY_AVAILABLE() 70 | 71 | template 72 | HFSM2_CONSTEXPR(14) 73 | void 74 | ControlT::pinLastTransition(const StateID stateId_, 75 | const Short index) noexcept 76 | { 77 | if (index != INVALID_SHORT) { 78 | HFSM2_ASSERT(index < TransitionSets::CAPACITY); 79 | 80 | if (!_core.registry.isActive(stateId_)) 81 | _core.transitionTargets[stateId_] = index; 82 | } 83 | } 84 | 85 | //------------------------------------------------------------------------------ 86 | 87 | template 88 | HFSM2_CONSTEXPR(14) 89 | const typename ControlT::Transition* 90 | ControlT::lastTransitionTo(const StateID stateId_) const noexcept { 91 | if (HFSM2_CHECKED(stateId_ < StateList::SIZE)) { 92 | const Short index = _core.transitionTargets[stateId_]; 93 | 94 | if (index < _core.previousTransitions.count()) 95 | return &_core.previousTransitions[index]; 96 | } 97 | 98 | return nullptr; 99 | } 100 | 101 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 102 | 103 | template 104 | HFSM2_CONSTEXPR(14) 105 | const typename ControlT::Transition* 106 | ControlT::lastTransition() const noexcept { 107 | HFSM2_ASSERT(_originId < _core.transitionTargets.CAPACITY); 108 | 109 | return lastTransitionTo(_originId); 110 | } 111 | 112 | #endif 113 | 114 | //////////////////////////////////////////////////////////////////////////////// 115 | 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/plan_0.hpp: -------------------------------------------------------------------------------- 1 | #if HFSM2_PLANS_AVAILABLE() 2 | 3 | namespace hfsm2 { 4 | namespace detail { 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | template 9 | class CPlanT { 10 | template 11 | friend class ControlT; 12 | 13 | template 14 | friend class PlanControlT; 15 | 16 | template 17 | friend class FullControlT; 18 | 19 | template 20 | friend class GuardControlT; 21 | 22 | template 23 | friend class R_; 24 | 25 | using Args = TArgs; 26 | using Context = typename Args::Context; 27 | using StateList = typename Args::StateList; 28 | using RegionList = typename Args::RegionList; 29 | 30 | static constexpr Long TASK_CAPACITY = Args::TASK_CAPACITY; 31 | 32 | public: 33 | using PlanData = PlanDataT; 34 | using Task = typename PlanData::Task; 35 | using TaskLinks = typename PlanData::TaskLinks; 36 | 37 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 38 | 39 | struct Iterator final { 40 | HFSM2_CONSTEXPR(14) Iterator(const CPlanT& plan) noexcept; 41 | 42 | HFSM2_CONSTEXPR(14) explicit operator bool() const noexcept; 43 | 44 | HFSM2_CONSTEXPR(14) void operator ++() noexcept; 45 | 46 | HFSM2_CONSTEXPR(11) bool operator != (const Iterator) const noexcept { return operator bool(); } 47 | 48 | HFSM2_CONSTEXPR(11) const Task& operator *() const noexcept { return _plan._planData.tasks[_curr]; } 49 | HFSM2_CONSTEXPR(11) const Task* operator ->() const noexcept { return &_plan._planData.tasks[_curr]; } 50 | 51 | HFSM2_CONSTEXPR(14) Long next() const noexcept; 52 | 53 | const CPlanT& _plan; 54 | Long _curr; 55 | Long _next; 56 | }; 57 | 58 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 59 | 60 | private: 61 | HFSM2_CONSTEXPR(11) CPlanT(const PlanData& planData, 62 | const RegionID regionId_) noexcept 63 | : _planData{planData} 64 | , _bounds{planData.taskBounds[regionId_]} 65 | {} 66 | 67 | template 68 | static 69 | HFSM2_CONSTEXPR(11) StateID stateId() noexcept { return index(); } 70 | 71 | template 72 | static 73 | HFSM2_CONSTEXPR(11) RegionID regionId() noexcept { return static_cast(index()); } 74 | 75 | public: 76 | HFSM2_CONSTEXPR(14) explicit operator bool() const noexcept; 77 | 78 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 79 | 80 | /// @brief Begin iteration over plan tasks 81 | /// @return CIterator to the first task 82 | HFSM2_CONSTEXPR(14) Iterator begin() noexcept { return Iterator{*this}; } 83 | 84 | /// @brief Iteration terminator 85 | /// @return Dummy Iterator 86 | HFSM2_CONSTEXPR(14) Iterator end () noexcept { return Iterator{*this}; } 87 | 88 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 89 | 90 | /// @brief First task 91 | /// @return First task 92 | HFSM2_CONSTEXPR(14) const Task& first() const noexcept; 93 | 94 | /// @brief Last task 95 | /// @return Last task 96 | HFSM2_CONSTEXPR(14) const Task& last() const noexcept; 97 | 98 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 99 | 100 | private: 101 | const PlanData& _planData; 102 | const Bounds& _bounds; 103 | }; 104 | 105 | //////////////////////////////////////////////////////////////////////////////// 106 | 107 | } 108 | } 109 | 110 | #endif 111 | 112 | #include "plan_0.inl" 113 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ${{ matrix.PLATFORM.OS }} 12 | 13 | env: 14 | CC: ${{ matrix.PLATFORM.CC }} 15 | CXX: ${{ matrix.PLATFORM.CXX }} 16 | 17 | strategy: 18 | matrix: 19 | PLATFORM: 20 | - { 21 | OS: ubuntu-22.04, 22 | CC: gcc-10, 23 | CXX: g++-10 24 | } 25 | - { 26 | OS: ubuntu-22.04, 27 | CC: gcc-11, 28 | CXX: g++-11 29 | } 30 | - { 31 | OS: ubuntu-22.04, 32 | CC: gcc-12, 33 | CXX: g++-12 34 | } 35 | - { 36 | OS: ubuntu-24.04, 37 | CC: gcc-13, 38 | CXX: g++-13 39 | } 40 | - { 41 | OS: ubuntu-24.04, 42 | CC: gcc-14, 43 | CXX: g++-14 44 | } 45 | - { 46 | OS: ubuntu-22.04, 47 | CC: clang-13, 48 | CXX: clang++-13 49 | } 50 | - { 51 | OS: ubuntu-22.04, 52 | CC: clang-14, 53 | CXX: clang++-14 54 | } 55 | - { 56 | OS: ubuntu-22.04, 57 | CC: clang-15, 58 | CXX: clang++-15, 59 | VERSION: '15' 60 | } 61 | - { 62 | OS: ubuntu-24.04, 63 | CC: clang-16, 64 | CXX: clang++-16, 65 | VERSION: '16' 66 | } 67 | - { 68 | OS: ubuntu-24.04, 69 | CC: clang-17, 70 | CXX: clang++-17, 71 | VERSION: '17' 72 | } 73 | - { 74 | OS: ubuntu-24.04, 75 | CC: clang-18, 76 | CXX: clang++-18, 77 | VERSION: '18' 78 | } 79 | - { 80 | OS: macos-13, 81 | CC: clang, 82 | CXX: clang++ 83 | } 84 | - { 85 | OS: macos-14, 86 | CC: clang, 87 | CXX: clang++ 88 | } 89 | - { 90 | OS: macos-15, 91 | CC: clang, 92 | CXX: clang++ 93 | } 94 | 95 | BUILD_CONFIG: [ Release, Debug ] 96 | 97 | steps: 98 | - uses: actions/checkout@v6 99 | 100 | - name: Install GCC on ubuntu 101 | if: | 102 | startsWith(matrix.PLATFORM.OS, 'ubuntu-') && 103 | startsWith(matrix.PLATFORM.CC, 'gcc-') && 104 | matrix.PLATFORM.INSTALL 105 | run: | 106 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 107 | sudo apt-get update 108 | sudo apt-get install ${{ matrix.PLATFORM.CC }} ${{ matrix.PLATFORM.CXX }} 109 | 110 | - name: Install Clang on ubuntu 111 | if: | 112 | startsWith(matrix.PLATFORM.OS, 'ubuntu-') && 113 | startsWith(matrix.PLATFORM.CC, 'clang-') && 114 | matrix.PLATFORM.VERSION 115 | uses: egor-tensin/setup-clang@v1 116 | with: 117 | version: ${{ matrix.PLATFORM.VERSION }} 118 | 119 | - name: Configure 120 | run: cmake -B ./build -DHFSM2_BUILD_TESTS=ON -DHFSM2_BUILD_EXAMPLES=ON -DCMAKE_BUILD_TYPE=${{ matrix.BUILD_CONFIG }} 121 | working-directory: ${{ github.workspace }} 122 | 123 | - name: Build 124 | run: cmake --build ./build --config ${{ matrix.BUILD_CONFIG }} 125 | working-directory: ${{ github.workspace }} 126 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root_3.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | HFSM2_CONSTEXPR(14) 8 | RC_, TA_>::RC_(Context& context 9 | HFSM2_IF_UTILITY_THEORY(, RNG& rng) 10 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger)) noexcept 11 | : Base{context 12 | HFSM2_IF_UTILITY_THEORY(, rng) 13 | HFSM2_IF_LOG_INTERFACE(, logger)} 14 | {} 15 | 16 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 17 | 18 | template 19 | HFSM2_CONSTEXPR(14) 20 | RC_, TA_>::RC_(PureContext&& context 21 | HFSM2_IF_UTILITY_THEORY(, RNG& rng) 22 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger)) noexcept 23 | : Base{move(context) 24 | HFSM2_IF_UTILITY_THEORY(, rng) 25 | HFSM2_IF_LOG_INTERFACE(, logger)} 26 | {} 27 | 28 | //////////////////////////////////////////////////////////////////////////////// 29 | 30 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 31 | 32 | template 33 | HFSM2_CONSTEXPR(14) 34 | RC_, TA_>::RC_(Context context 35 | , RNG& rng 36 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger)) noexcept 37 | : Base{context 38 | , rng 39 | HFSM2_IF_LOG_INTERFACE(, logger)} 40 | {} 41 | 42 | #else 43 | 44 | template 45 | HFSM2_CONSTEXPR(14) 46 | RC_, TA_>::RC_(Context context 47 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger)) noexcept 48 | : Base{context 49 | HFSM2_IF_LOG_INTERFACE(, logger)} 50 | {} 51 | 52 | #endif 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | 56 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 57 | 58 | template 59 | HFSM2_CONSTEXPR(14) 60 | RC_, TA_>::RC_(RNG& rng 61 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger)) noexcept 62 | : Base{static_cast(*this) 63 | , rng 64 | HFSM2_IF_LOG_INTERFACE(, logger)} 65 | {} 66 | 67 | #else 68 | 69 | template 70 | HFSM2_CONSTEXPR(14) 71 | RC_, TA_>::RC_(HFSM2_IF_LOG_INTERFACE(Logger* const logger)) noexcept 72 | : Base{static_cast(*this) 73 | HFSM2_IF_LOG_INTERFACE(, logger)} 74 | {} 75 | 76 | #endif 77 | 78 | //////////////////////////////////////////////////////////////////////////////// 79 | 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | project(hfsm2 VERSION 2.8.0 LANGUAGES CXX) 4 | 5 | # Create an interface library (header-only) 6 | add_library(${PROJECT_NAME} INTERFACE) 7 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 8 | 9 | # Set include directories for users of this library 10 | target_include_directories(${PROJECT_NAME} 11 | INTERFACE 12 | $ 13 | $ 14 | ) 15 | 16 | # Set C++11 requirement for users of this library 17 | target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11) 18 | 19 | # Install the library 20 | install(TARGETS ${PROJECT_NAME} 21 | EXPORT ${PROJECT_NAME}Targets 22 | INCLUDES DESTINATION include 23 | ) 24 | 25 | # Install the headers 26 | install(DIRECTORY include/ DESTINATION include) 27 | 28 | # Export the targets 29 | export(EXPORT ${PROJECT_NAME}Targets 30 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Targets.cmake" 31 | NAMESPACE ${PROJECT_NAME}:: 32 | ) 33 | 34 | # Create and install package configuration files 35 | include(CMakePackageConfigHelpers) 36 | write_basic_package_version_file( 37 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 38 | VERSION ${PROJECT_VERSION} 39 | COMPATIBILITY SameMajorVersion 40 | ) 41 | 42 | # Simple config file approach (no template required) 43 | file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 44 | "include(CMakeFindDependencyMacro) 45 | include(\"\${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}Targets.cmake\") 46 | ") 47 | 48 | # Install the configuration files 49 | install(FILES 50 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 51 | "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 52 | DESTINATION lib/cmake/${PROJECT_NAME} 53 | ) 54 | 55 | install(EXPORT ${PROJECT_NAME}Targets 56 | FILE ${PROJECT_NAME}Targets.cmake 57 | NAMESPACE ${PROJECT_NAME}:: 58 | DESTINATION lib/cmake/${PROJECT_NAME} 59 | ) 60 | 61 | # Testing section 62 | option(HFSM2_BUILD_TESTS "Build the HFSM2 tests" OFF) 63 | if(HFSM2_BUILD_TESTS) 64 | enable_testing() 65 | 66 | file(GLOB TEST_SOURCE_FILES 67 | "${CMAKE_CURRENT_SOURCE_DIR}/test/*.cpp" 68 | "${CMAKE_CURRENT_SOURCE_DIR}/test/*/*.cpp") 69 | add_executable(${PROJECT_NAME}_test ${TEST_SOURCE_FILES}) 70 | 71 | target_include_directories(${PROJECT_NAME}_test PRIVATE 72 | "${CMAKE_CURRENT_SOURCE_DIR}/external" 73 | "${CMAKE_CURRENT_SOURCE_DIR}/include") 74 | 75 | target_compile_features(${PROJECT_NAME}_test PRIVATE cxx_std_11) 76 | 77 | if(MSVC) 78 | target_compile_options(${PROJECT_NAME}_test PRIVATE /W4 /WX) 79 | else() 80 | target_compile_options(${PROJECT_NAME}_test PRIVATE -Werror -Wall -Wextra -Wpedantic -Wshadow -Wold-style-cast) 81 | endif() 82 | 83 | add_test(NAME ${PROJECT_NAME}_test COMMAND ${PROJECT_NAME}_test) 84 | 85 | add_custom_command(TARGET ${PROJECT_NAME}_test 86 | POST_BUILD 87 | COMMAND ${PROJECT_NAME}_test) 88 | endif() 89 | 90 | # Examples section 91 | option(HFSM2_BUILD_EXAMPLES "Build the examples" OFF) 92 | if(HFSM2_BUILD_EXAMPLES) 93 | # Check if examples directory exists 94 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples") 95 | # Get all subdirectories in examples folder 96 | file(GLOB EXAMPLE_DIRS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/examples" "${CMAKE_CURRENT_SOURCE_DIR}/examples/*") 97 | 98 | foreach(EXAMPLE_DIR ${EXAMPLE_DIRS}) 99 | # Check if the subdirectory contains a CMakeLists.txt 100 | if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples/${EXAMPLE_DIR}/CMakeLists.txt") 101 | message(STATUS "Adding example: ${EXAMPLE_DIR}") 102 | add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/examples/${EXAMPLE_DIR}") 103 | endif() 104 | endforeach() 105 | else() 106 | message(STATUS "Examples directory not found, skipping examples") 107 | endif() 108 | endif() -------------------------------------------------------------------------------- /development/hfsm2/detail/root/registry_2.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template < 7 | typename TConfig 8 | , typename TStateList 9 | , typename TRegionList 10 | , Long NCompoCount 11 | , typename TReactOrder 12 | HFSM2_IF_SERIALIZATION(, Long NSerialBits) 13 | HFSM2_IF_PLANS(, Long NTaskCapacity) 14 | , typename TPayload 15 | > 16 | struct RegistryT< 17 | ArgsT< 18 | TConfig 19 | , TStateList 20 | , TRegionList 21 | , NCompoCount 22 | , 0 23 | , 0 24 | , TReactOrder 25 | HFSM2_IF_SERIALIZATION(, NSerialBits) 26 | HFSM2_IF_PLANS(, NTaskCapacity) 27 | , TPayload 28 | > 29 | > final 30 | { 31 | using StateList = TStateList; 32 | using RegionList = TRegionList; 33 | 34 | static constexpr Long STATE_COUNT = StateList ::SIZE; 35 | static constexpr Long REGION_COUNT = RegionList::SIZE; 36 | static constexpr Short COMPO_COUNT = NCompoCount; 37 | 38 | using Payload = TPayload; 39 | using Transition = TransitionT; 40 | 41 | using StateParents = StaticArrayT; 42 | using CompoParents = StaticArrayT; 43 | using RegionHeads = StaticArrayT; 44 | using RegionSizes = StaticArrayT; 45 | 46 | using CompoForks = StaticArrayT; 47 | using OrthoForks = BitArrayT <0>; 48 | using CompoRemains = BitArrayT < COMPO_COUNT >; 49 | 50 | #if HFSM2_PLANS_AVAILABLE() 51 | using CompoStatuses = BitArrayT < COMPO_COUNT >; 52 | #endif 53 | 54 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 55 | 56 | struct BackUp final { 57 | CompoForks compoRequested; 58 | }; 59 | 60 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 61 | 62 | HFSM2_CONSTEXPR(14) Prong activeSubState (const StateID stateId) const noexcept; 63 | 64 | HFSM2_CONSTEXPR(11) bool isActive () const noexcept; 65 | HFSM2_CONSTEXPR(14) bool isActive (const StateID stateId) const noexcept; 66 | HFSM2_CONSTEXPR(14) bool isResumable (const StateID stateId) const noexcept; 67 | 68 | HFSM2_CONSTEXPR(14) bool isPendingEnter (const StateID stateId) const noexcept; 69 | HFSM2_CONSTEXPR(14) bool isPendingChange (const StateID stateId) const noexcept; 70 | HFSM2_CONSTEXPR(14) bool isPendingExit (const StateID stateId) const noexcept; 71 | 72 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 73 | 74 | HFSM2_CONSTEXPR(14) const Parent& forkParent(const ForkID forkId) const noexcept; 75 | 76 | HFSM2_CONSTEXPR(14) void requestImmediate (const Transition& request) noexcept; 77 | 78 | HFSM2_CONSTEXPR(14) void requestScheduled (const StateID stateId) noexcept; 79 | 80 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 81 | 82 | HFSM2_CONSTEXPR(14) void clearRequests () noexcept; 83 | HFSM2_CONSTEXPR(14) void clear () noexcept; 84 | 85 | HFSM2_CONSTEXPR(11) bool empty () const noexcept; 86 | 87 | HFSM2_CONSTEXPR(14) void backup ( BackUp& copy) const noexcept; 88 | HFSM2_CONSTEXPR(14) void restore (const BackUp& copy) noexcept; 89 | 90 | HFSM2_CONSTEXPR(11) bool operator != (const BackUp& copy) const noexcept; 91 | 92 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 93 | 94 | StateParents stateParents; 95 | CompoParents compoParents; 96 | RegionHeads regionHeads; 97 | RegionSizes regionSizes; 98 | 99 | CompoForks compoRequested{INVALID_PRONG}; 100 | OrthoForks orthoRequested; 101 | CompoForks compoActive {INVALID_PRONG}; 102 | CompoForks compoResumable{INVALID_PRONG}; 103 | 104 | CompoRemains compoRemains; 105 | 106 | #if HFSM2_PLANS_AVAILABLE() 107 | CompoStatuses compoStatuses; 108 | #endif 109 | }; 110 | 111 | //////////////////////////////////////////////////////////////////////////////// 112 | 113 | } 114 | } 115 | 116 | #include "registry_2.inl" 117 | -------------------------------------------------------------------------------- /test/test_contexts_random.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_UTILITY_THEORY 5 | #include "tools.hpp" 6 | 7 | namespace test_contexts_random { 8 | 9 | //------------------------------------------------------------------------------ 10 | 11 | using Context = int; 12 | 13 | struct DummyRNG { 14 | inline float next() { return 0.0f; } 15 | }; 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | 19 | namespace value_context { 20 | 21 | using Config = hfsm2::Config 22 | ::ContextT 23 | ::RandomT; 24 | 25 | using M = hfsm2::MachineT; 26 | 27 | struct A; 28 | struct B; 29 | 30 | using FSM = M::PeerRoot; 31 | 32 | struct A : FSM::State {}; 33 | struct B : FSM::State {}; 34 | 35 | } 36 | 37 | //////////////////////////////////////////////////////////////////////////////// 38 | 39 | namespace reference_context { 40 | 41 | using Config = hfsm2::Config 42 | ::ContextT 43 | ::RandomT; 44 | 45 | using M = hfsm2::MachineT; 46 | 47 | struct A; 48 | struct B; 49 | 50 | using FSM = M::PeerRoot; 51 | 52 | struct A : FSM::State {}; 53 | struct B : FSM::State {}; 54 | 55 | } 56 | 57 | //////////////////////////////////////////////////////////////////////////////// 58 | 59 | namespace pointer_context { 60 | 61 | using Config = hfsm2::Config 62 | ::ContextT 63 | ::RandomT; 64 | 65 | using M = hfsm2::MachineT; 66 | 67 | struct A; 68 | struct B; 69 | 70 | using FSM = M::PeerRoot; 71 | 72 | struct A : FSM::State {}; 73 | struct B : FSM::State {}; 74 | 75 | } 76 | 77 | //////////////////////////////////////////////////////////////////////////////// 78 | 79 | namespace no_context { 80 | 81 | using Config = hfsm2::Config 82 | ::RandomT; 83 | 84 | using M = hfsm2::MachineT; 85 | 86 | struct A; 87 | struct B; 88 | 89 | using FSM = M::PeerRoot; 90 | 91 | struct A : FSM::State {}; 92 | struct B : FSM::State {}; 93 | 94 | } 95 | 96 | //////////////////////////////////////////////////////////////////////////////// 97 | 98 | TEST_CASE("FSM.Contexts Random") { 99 | Context primary = 7; 100 | Context secondary = -39; 101 | DummyRNG rng; 102 | 103 | // context is a value 104 | { 105 | using Instance = value_context::FSM::Instance; 106 | static_assert(std::is_same::value, ""); 107 | 108 | //Instance defaultConstructed; 109 | 110 | const Instance constant{primary, rng}; 111 | REQUIRE(constant.context() == primary); 112 | 113 | Instance machine{primary, rng}; 114 | REQUIRE(machine.context() == primary); 115 | } 116 | 117 | // context is a reference 118 | { 119 | using Instance = reference_context::FSM::Instance; 120 | static_assert(std::is_same::value, ""); 121 | 122 | const Instance constant{primary, rng}; 123 | REQUIRE(constant.context() == primary); 124 | 125 | Instance machine{primary, rng}; 126 | REQUIRE(machine.context() == primary); 127 | } 128 | 129 | // context is a pointer 130 | { 131 | using Instance = pointer_context::FSM::Instance; 132 | static_assert(std::is_same::value, ""); 133 | 134 | pointer_context::FSM::Instance defaultConstructed(nullptr, rng); 135 | REQUIRE(defaultConstructed.context() == nullptr); 136 | 137 | const Instance constant{&primary, rng}; 138 | REQUIRE(constant.context() == &primary); 139 | 140 | Instance machine{&primary, rng}; 141 | REQUIRE(machine.context() == &primary); 142 | 143 | machine.setContext(&secondary); 144 | REQUIRE(machine.context() == &secondary); 145 | } 146 | 147 | // empty context 148 | { 149 | no_context::FSM::Instance defaultConstructed(rng); 150 | static_assert(std::is_same::value, ""); 151 | } 152 | } 153 | 154 | //////////////////////////////////////////////////////////////////////////////// 155 | 156 | } 157 | -------------------------------------------------------------------------------- /development/hfsm2/detail/containers/array.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | template 8 | HFSM2_CONSTEXPR(14) 9 | T& 10 | StaticArrayT::operator[] (const N index) noexcept { 11 | HFSM2_ASSERT(0 <= index && index < CAPACITY); 12 | 13 | return _items[static_cast(index)]; 14 | } 15 | 16 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 17 | 18 | template 19 | template 20 | HFSM2_CONSTEXPR(14) 21 | const T& 22 | StaticArrayT::operator[] (const N index) const noexcept { 23 | HFSM2_ASSERT(0 <= index && index < CAPACITY); 24 | 25 | return _items[static_cast(index)]; 26 | } 27 | 28 | //------------------------------------------------------------------------------ 29 | 30 | template 31 | HFSM2_CONSTEXPR(14) 32 | void 33 | StaticArrayT::fill(const Item filler) noexcept { 34 | for (Item& item : _items) 35 | item = filler; 36 | } 37 | 38 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 39 | 40 | template 41 | HFSM2_CONSTEXPR(14) 42 | bool 43 | StaticArrayT::operator != (const Array& other) const noexcept { 44 | for (unsigned i = 0; i < CAPACITY; ++i) 45 | if (_items[i] != other._items[i]) 46 | return true; 47 | 48 | return false; 49 | } 50 | 51 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 52 | 53 | template 54 | HFSM2_CONSTEXPR(14) 55 | bool 56 | StaticArrayT::empty() const noexcept { 57 | for (const Item& item : _items) 58 | if (item != filler()) 59 | return false; 60 | 61 | return true; 62 | } 63 | 64 | //////////////////////////////////////////////////////////////////////////////// 65 | 66 | template 67 | template 68 | HFSM2_CONSTEXPR(14) 69 | typename DynamicArrayT::Index 70 | DynamicArrayT::emplace(const TArgs&... args) noexcept { 71 | HFSM2_ASSERT(_count < CAPACITY); 72 | 73 | new (&_items[_count]) Item{args...}; 74 | 75 | return _count++; 76 | } 77 | 78 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 79 | 80 | template 81 | template 82 | HFSM2_CONSTEXPR(14) 83 | typename DynamicArrayT::Index 84 | DynamicArrayT::emplace(TArgs&&... args) noexcept { 85 | HFSM2_ASSERT(_count < CAPACITY); 86 | 87 | new (&_items[_count]) Item{::hfsm2::forward(args)...}; 88 | 89 | return _count++; 90 | } 91 | 92 | //------------------------------------------------------------------------------ 93 | 94 | template 95 | template 96 | HFSM2_CONSTEXPR(14) 97 | typename DynamicArrayT::Item& 98 | DynamicArrayT::operator[] (const N index) noexcept { 99 | HFSM2_ASSERT(0 <= index && index < _count); 100 | 101 | return _items[static_cast(index)]; 102 | } 103 | 104 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 105 | 106 | template 107 | template 108 | HFSM2_CONSTEXPR(14) 109 | const typename DynamicArrayT::Item& 110 | DynamicArrayT::operator[] (const N index) const noexcept { 111 | HFSM2_ASSERT(0 <= index && index < _count); 112 | 113 | return _items[static_cast(index)]; 114 | } 115 | 116 | //------------------------------------------------------------------------------ 117 | // SPECIFIC 118 | // SPECIFIC 119 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 120 | 121 | template 122 | template 123 | HFSM2_CONSTEXPR(14) 124 | DynamicArrayT& 125 | DynamicArrayT::operator += (const DynamicArrayT& other) noexcept { 126 | for (const auto& item : other) 127 | emplace(item); 128 | 129 | return *this; 130 | } 131 | 132 | //////////////////////////////////////////////////////////////////////////////// 133 | 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /development/hfsm2/detail/structure/ancestors_2.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | #if HFSM2_PLANS_AVAILABLE() 7 | 8 | template 9 | HFSM2_CONSTEXPR(14) 10 | void 11 | A_::planSucceeded(FullControl& control) noexcept { 12 | control.succeed(); 13 | } 14 | 15 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 16 | 17 | template 18 | HFSM2_CONSTEXPR(14) 19 | void 20 | A_::planFailed(FullControl& control) noexcept { 21 | control.fail(); 22 | } 23 | 24 | #endif 25 | 26 | //------------------------------------------------------------------------------ 27 | 28 | template 29 | HFSM2_CONSTEXPR(14) 30 | void 31 | A_::wideEntryGuard(GuardControl& control) noexcept { 32 | First:: entryGuard(control); 33 | } 34 | 35 | //------------------------------------------------------------------------------ 36 | 37 | template 38 | HFSM2_CONSTEXPR(14) 39 | void 40 | A_::wideEnter(PlanControl& control) noexcept { 41 | First:: enter(control); 42 | } 43 | 44 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 45 | 46 | template 47 | HFSM2_CONSTEXPR(14) 48 | void 49 | A_::wideReenter(PlanControl& control) noexcept { 50 | First:: reenter(control); 51 | } 52 | 53 | //------------------------------------------------------------------------------ 54 | 55 | template 56 | HFSM2_CONSTEXPR(14) 57 | void 58 | A_::widePreUpdate(FullControl& control) noexcept { 59 | First:: preUpdate(control); 60 | } 61 | 62 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 63 | 64 | template 65 | HFSM2_CONSTEXPR(14) 66 | void 67 | A_::wideUpdate(FullControl& control) noexcept { 68 | First:: update(control); 69 | } 70 | 71 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 72 | 73 | template 74 | HFSM2_CONSTEXPR(14) 75 | void 76 | A_::widePostUpdate(FullControl& control) noexcept { 77 | First:: postUpdate(control); 78 | } 79 | 80 | //------------------------------------------------------------------------------ 81 | 82 | template 83 | template 84 | HFSM2_CONSTEXPR(14) 85 | void 86 | A_::widePreReact(const TEvent& event, 87 | EventControl& control) noexcept 88 | { 89 | First:: preReact(event, control); 90 | } 91 | 92 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 93 | 94 | template 95 | template 96 | HFSM2_CONSTEXPR(14) 97 | void 98 | A_::wideReact(const TEvent& event, 99 | EventControl& control) noexcept 100 | { 101 | First:: react(event, control); 102 | } 103 | 104 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 105 | 106 | template 107 | template 108 | HFSM2_CONSTEXPR(14) 109 | void 110 | A_::widePostReact(const TEvent& event, 111 | EventControl& control) noexcept 112 | { 113 | First:: postReact(event, control); 114 | } 115 | 116 | //------------------------------------------------------------------------------ 117 | 118 | template 119 | template 120 | HFSM2_CONSTEXPR(14) 121 | void 122 | A_::wideQuery(TEvent& event, 123 | ConstControl& control) const noexcept 124 | { 125 | First:: query(event, control); 126 | } 127 | 128 | //------------------------------------------------------------------------------ 129 | 130 | template 131 | HFSM2_CONSTEXPR(14) 132 | void 133 | A_::wideExitGuard(GuardControl& control) noexcept { 134 | First:: exitGuard(control); 135 | } 136 | 137 | //------------------------------------------------------------------------------ 138 | 139 | template 140 | HFSM2_CONSTEXPR(14) 141 | void 142 | A_::wideExit(PlanControl& control) noexcept { 143 | First:: exit(control); 144 | } 145 | 146 | //////////////////////////////////////////////////////////////////////////////// 147 | 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /test/test_composite_bst.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_VERBOSE_DEBUG_LOG 5 | #include "tools.hpp" 6 | 7 | using namespace test_tools; 8 | 9 | namespace test_composite_bst { 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | using M = hfsm2::Machine; 14 | 15 | //------------------------------------------------------------------------------ 16 | 17 | #define S(s) struct s 18 | 19 | using FSM = M::Root; 31 | 32 | #undef S 33 | 34 | //------------------------------------------------------------------------------ 35 | 36 | static_assert(FSM::regionId() == 0, ""); 37 | 38 | static_assert(FSM::stateId() == 0, ""); 39 | static_assert(FSM::stateId() == 1, ""); 40 | static_assert(FSM::stateId() == 2, ""); 41 | static_assert(FSM::stateId() == 3, ""); 42 | static_assert(FSM::stateId() == 4, ""); 43 | static_assert(FSM::stateId() == 5, ""); 44 | static_assert(FSM::stateId() == 6, ""); 45 | static_assert(FSM::stateId() == 7, ""); 46 | static_assert(FSM::stateId() == 8, ""); 47 | static_assert(FSM::stateId() == 9, ""); 48 | static_assert(FSM::stateId() == 10, ""); 49 | 50 | //////////////////////////////////////////////////////////////////////////////// 51 | 52 | struct Apex : FSM::State {}; 53 | struct S0 : FSM::State {}; 54 | struct S1 : FSM::State {}; 55 | struct S2 : FSM::State {}; 56 | struct S3 : FSM::State {}; 57 | struct S4 : FSM::State {}; 58 | struct S5 : FSM::State {}; 59 | struct S6 : FSM::State {}; 60 | struct S7 : FSM::State {}; 61 | struct S8 : FSM::State {}; 62 | struct S9 : FSM::State {}; 63 | 64 | //////////////////////////////////////////////////////////////////////////////// 65 | 66 | static_assert(FSM::Instance::Info::STATE_COUNT == 11, ""); 67 | static_assert(FSM::Instance::Info::REGION_COUNT == 1, ""); 68 | static_assert(FSM::Instance::Info::COMPO_COUNT == 1, ""); 69 | static_assert(FSM::Instance::Info::COMPO_PRONGS == 10, ""); 70 | static_assert(FSM::Instance::Info::ORTHO_COUNT == 0, ""); 71 | static_assert(FSM::Instance::Info::ORTHO_UNITS == 0, ""); 72 | 73 | //////////////////////////////////////////////////////////////////////////////// 74 | 75 | void step0(FSM::Instance& machine, Logger& logger) { 76 | logger.assertSequence({ 77 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 78 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 79 | 80 | { FSM::stateId(), Event::Type::ENTER }, 81 | { FSM::stateId(), Event::Type::ENTER }, 82 | }); 83 | 84 | assertActive(machine, { 85 | FSM::stateId(), 86 | FSM::stateId(), 87 | }); 88 | 89 | assertResumable(machine, {}); 90 | } 91 | 92 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 93 | 94 | void step1(FSM::Instance& machine, Logger& logger) { 95 | machine.immediateChangeTo(); 96 | 97 | logger.assertSequence({ 98 | { Event::Type::CHANGE, FSM::stateId() }, 99 | 100 | { FSM::stateId(), Event::Type::EXIT_GUARD }, 101 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 102 | 103 | { FSM::stateId(), Event::Type::EXIT }, 104 | { FSM::stateId(), Event::Type::ENTER }, 105 | }); 106 | 107 | assertActive(machine, { 108 | FSM::stateId(), 109 | FSM::stateId(), 110 | }); 111 | 112 | assertResumable(machine, { 113 | FSM::stateId(), 114 | }); 115 | } 116 | 117 | //------------------------------------------------------------------------------ 118 | 119 | TEST_CASE("FSM.Composite BST") { 120 | Logger logger; 121 | 122 | { 123 | FSM::Instance machine{&logger}; 124 | step0(machine, logger); 125 | step1(machine, logger); 126 | } 127 | 128 | logger.assertSequence({ 129 | { FSM::stateId(), Event::Type::EXIT }, 130 | { hfsm2::ROOT_ID , Event::Type::EXIT }, 131 | }); 132 | } 133 | 134 | //////////////////////////////////////////////////////////////////////////////// 135 | 136 | } 137 | -------------------------------------------------------------------------------- /development/hfsm2/detail/shared/type_list.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | // SPECIFIC 6 | 7 | template 8 | struct TL_ final { 9 | static constexpr Long SIZE = sizeof...(Ts); 10 | }; 11 | 12 | // SPECIFIC 13 | //------------------------------------------------------------------------------ 14 | 15 | template 16 | struct Const { 17 | static constexpr Long VALUE = N; 18 | }; 19 | 20 | //------------------------------------------------------------------------------ 21 | 22 | template 23 | struct PrependT; 24 | 25 | template 26 | struct PrependT> final { 27 | using Type = TL_; 28 | }; 29 | 30 | template 31 | using PrependTypes = typename PrependT::Type; 32 | 33 | //------------------------------------------------------------------------------ 34 | 35 | template 36 | struct MergeT; 37 | 38 | template 39 | struct MergeT, TL_> final { 40 | using Type = TL_; 41 | }; 42 | 43 | template 44 | using Merge = typename MergeT::Type; 45 | 46 | //------------------------------------------------------------------------------ 47 | 48 | template 49 | struct LowerT; 50 | 51 | template 52 | using LowerTypes = typename LowerT::Type; 53 | 54 | template 55 | struct LowerT final { 56 | using LTypeList = typename LowerT::Type; 57 | 58 | using Type = Conditional< 59 | (NIndex < NHalf), 60 | PrependTypes, 61 | LTypeList 62 | >; 63 | }; 64 | 65 | template 66 | struct LowerT final { 67 | using Type = TL_<>; 68 | }; 69 | 70 | template 71 | using LHalfTypes = LowerTypes; 72 | 73 | //------------------------------------------------------------------------------ 74 | 75 | template 76 | struct UpperT; 77 | 78 | template 79 | using UpperTypes = typename UpperT::Type; 80 | 81 | template 82 | struct UpperT final { 83 | using Type = Conditional< 84 | (NIndex < NHalf), 85 | UpperTypes, 86 | TL_ 87 | >; 88 | }; 89 | 90 | template 91 | struct UpperT final { 92 | using Type = TL_<>; 93 | }; 94 | 95 | template 96 | using RHalfTypes = UpperTypes; 97 | 98 | //------------------------------------------------------------------------------ 99 | 100 | template 101 | struct FindImpl 102 | : Const 103 | {}; 104 | 105 | template 106 | struct FindImpl 107 | : FindImpl 108 | {}; 109 | 110 | template 111 | struct FindImpl 112 | : Const 113 | {}; 114 | 115 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 116 | 117 | template 118 | struct Find; 119 | 120 | template 121 | struct Find, T> final 122 | : FindImpl<0, T, Ts...> 123 | {}; 124 | 125 | //////////////////////////////////////////////////////////////////////////////// 126 | // SPECIFIC 127 | 128 | } 129 | 130 | template 131 | constexpr Long index () noexcept { return detail::Find::VALUE; } 132 | 133 | template 134 | constexpr bool contains() noexcept { return index() != INVALID_LONG; } 135 | 136 | // SPECIFIC 137 | //------------------------------------------------------------------------------ 138 | 139 | } 140 | -------------------------------------------------------------------------------- /development/hfsm2/machine_dev.hpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // 2.10.0 (2025-11-30) 3 | // 4 | // Created by Andrew Gresyk 5 | // 6 | // Licensed under the MIT License; 7 | // you may not use this file except in compliance with the License. 8 | // 9 | // 10 | // MIT License 11 | // 12 | // Copyright (c) 2025 13 | // 14 | // Permission is hereby granted, free of charge, to any person obtaining a copy 15 | // of this software and associated documentation files (the "Software"), to deal 16 | // in the Software without restriction, including without limitation the rights 17 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | // copies of the Software, and to permit persons to whom the Software is 19 | // furnished to do so, subject to the following conditions: 20 | // 21 | // The above copyright notice and this permission notice shall be included in all 22 | // copies or substantial portions of the Software. 23 | // 24 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | // SOFTWARE. 31 | 32 | #pragma once 33 | 34 | #define HFSM2_VERSION_MAJOR 2 35 | #define HFSM2_VERSION_MINOR 10 36 | #define HFSM2_VERSION_PATCH 0 37 | 38 | #define HFSM2_VERSION (10000 * HFSM2_VERSION_MAJOR + 100 * HFSM2_VERSION_MINOR + HFSM2_VERSION_PATCH) 39 | 40 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 41 | 42 | #include // uint32_t, uint64_t 43 | #include // memcpy_s() 44 | 45 | #include 46 | #ifndef HFSM2_DISABLE_TYPEINDEX 47 | #include 48 | #endif 49 | 50 | #if defined _DEBUG && _MSC_VER 51 | #include // __debugbreak() 52 | #endif 53 | 54 | //------------------------------------------------------------------------------ 55 | 56 | #include "detail/shared/macros_on.hpp" 57 | 58 | #include "detail/shared/utility.hpp" 59 | #include "detail/shared/iterator.hpp" 60 | #include "detail/shared/bit_stream.hpp" 61 | #include "detail/shared/random.hpp" 62 | #include "detail/shared/type_list.hpp" 63 | 64 | #include "detail/containers/array.hpp" 65 | #include "detail/containers/bit_array.hpp" 66 | 67 | #include "detail/features/transition.hpp" 68 | #include "detail/features/logger_interface.hpp" 69 | #include "detail/features/structure_report.hpp" 70 | #include "detail/features/task.hpp" 71 | #include "detail/features/task_list.hpp" 72 | 73 | #include "detail/root/registry_1.hpp" 74 | #include "detail/root/registry_2.hpp" 75 | #include "detail/root/plan_data.hpp" 76 | #include "detail/root/plan_0.hpp" 77 | #include "detail/root/plan_1.hpp" 78 | #include "detail/root/plan_2.hpp" 79 | #include "detail/root/core.hpp" 80 | #include "detail/root/control_0.hpp" 81 | #include "detail/root/control_1.hpp" 82 | #include "detail/root/control_2.hpp" 83 | #include "detail/root/control_3.hpp" 84 | #include "detail/root/control_4.hpp" 85 | #include "detail/root/control_5.hpp" 86 | 87 | #include "detail/structure/base.hpp" 88 | #include "detail/structure/ancestors_1.hpp" 89 | #include "detail/structure/ancestors_2.hpp" 90 | #include "detail/structure/state_1.hpp" 91 | #include "detail/structure/state_2.hpp" 92 | #include "detail/structure/forward.hpp" 93 | #include "detail/structure/reactions.inl" 94 | #include "detail/structure/composite_sub_1.hpp" 95 | #include "detail/structure/composite_sub_2.hpp" 96 | #include "detail/structure/composite.hpp" 97 | #include "detail/structure/orthogonal_sub_1.hpp" 98 | #include "detail/structure/orthogonal_sub_2.hpp" 99 | #include "detail/structure/orthogonal.hpp" 100 | 101 | #include "detail/config.hpp" 102 | #include "detail/root_0.hpp" 103 | #include "detail/root_1.hpp" 104 | #include "detail/root_2.hpp" 105 | #include "detail/root_3.hpp" 106 | #include "detail/root_4.hpp" 107 | 108 | #include "detail/shared/macros_off.hpp" 109 | -------------------------------------------------------------------------------- /development/hfsm2/detail/structure/ancestors_1.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | HFSM2_CONSTEXPR(14) 8 | void 9 | A_::wideEntryGuard(GuardControl& control) noexcept { 10 | First:: entryGuard(control); 11 | Rest ::wideEntryGuard(control); 12 | } 13 | 14 | //------------------------------------------------------------------------------ 15 | 16 | template 17 | HFSM2_CONSTEXPR(14) 18 | void 19 | A_::wideEnter(PlanControl& control) noexcept { 20 | First:: enter(control); 21 | Rest ::wideEnter(control); 22 | } 23 | 24 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 25 | 26 | template 27 | HFSM2_CONSTEXPR(14) 28 | void 29 | A_::wideReenter(PlanControl& control) noexcept { 30 | First:: reenter(control); 31 | Rest ::wideReenter(control); 32 | } 33 | 34 | //------------------------------------------------------------------------------ 35 | 36 | template 37 | HFSM2_CONSTEXPR(14) 38 | void 39 | A_::widePreUpdate(FullControl& control) noexcept { 40 | First:: preUpdate(control); 41 | Rest ::widePreUpdate(control); 42 | } 43 | 44 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 45 | 46 | template 47 | HFSM2_CONSTEXPR(14) 48 | void 49 | A_::wideUpdate(FullControl& control) noexcept { 50 | First:: update(control); 51 | Rest ::wideUpdate(control); 52 | } 53 | 54 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 55 | 56 | template 57 | HFSM2_CONSTEXPR(14) 58 | void 59 | A_::widePostUpdate(FullControl& control) noexcept { 60 | Rest ::widePostUpdate(control); 61 | First:: postUpdate(control); 62 | } 63 | 64 | //------------------------------------------------------------------------------ 65 | 66 | template 67 | template 68 | HFSM2_CONSTEXPR(14) 69 | void 70 | A_::widePreReact(const TEvent& event, 71 | EventControl& control) noexcept 72 | { 73 | First:: preReact(event, control); 74 | Rest ::widePreReact(event, control); 75 | } 76 | 77 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 78 | 79 | template 80 | template 81 | HFSM2_CONSTEXPR(14) 82 | void 83 | A_::wideReact(const TEvent& event, 84 | EventControl& control) noexcept 85 | { 86 | First:: react(event, control); 87 | Rest ::wideReact(event, control); 88 | } 89 | 90 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 91 | 92 | template 93 | template 94 | HFSM2_CONSTEXPR(14) 95 | void 96 | A_::widePostReact(const TEvent& event, 97 | EventControl& control) noexcept 98 | { 99 | Rest ::widePostReact(event, control); 100 | First:: postReact(event, control); 101 | } 102 | 103 | //------------------------------------------------------------------------------ 104 | 105 | template 106 | template 107 | HFSM2_CONSTEXPR(14) 108 | void 109 | A_::wideQuery(TEvent& event, 110 | ConstControl& control) const noexcept 111 | { 112 | First:: query(event, control); 113 | Rest ::wideQuery(event, control); 114 | } 115 | 116 | //------------------------------------------------------------------------------ 117 | 118 | template 119 | HFSM2_CONSTEXPR(14) 120 | void 121 | A_::wideExitGuard(GuardControl& control) noexcept { 122 | Rest ::wideExitGuard(control); 123 | First:: exitGuard(control); 124 | } 125 | 126 | //------------------------------------------------------------------------------ 127 | 128 | template 129 | HFSM2_CONSTEXPR(14) 130 | void 131 | A_::wideExit(PlanControl& control) noexcept { 132 | Rest ::wideExit(control); 133 | First:: exit(control); 134 | } 135 | 136 | //////////////////////////////////////////////////////////////////////////////// 137 | 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /test/shared/test_task_list.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_PLANS 5 | #include "../tools.hpp" 6 | 7 | namespace test_task_list { 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | using List = hfsm2::detail::TaskListT; 12 | 13 | //------------------------------------------------------------------------------ 14 | 15 | TEST_CASE("Shared.List<>") { 16 | List list; 17 | constexpr auto CAPACITY = List::CAPACITY; 18 | 19 | REQUIRE(list.count() == 0); 20 | 21 | WHEN("fill, delete and re-insert an element") { 22 | for (List::Index i = 0; i < CAPACITY; ++i) { 23 | const auto index = list.emplace(static_cast(i), 24 | static_cast(i), 25 | hfsm2::TransitionType::COUNT); 26 | 27 | REQUIRE(index == i); 28 | REQUIRE(list.count() == i + 1); 29 | } 30 | 31 | for (List::Index i = 0; i < CAPACITY; ++i) 32 | REQUIRE(list[i] == hfsm2::detail::TaskBase{static_cast(i), 33 | static_cast(i), 34 | hfsm2::TransitionType::COUNT}); 35 | 36 | THEN("at the start") { 37 | REQUIRE(list.count() == CAPACITY); 38 | 39 | list.remove(0); 40 | REQUIRE(list.count() == CAPACITY - 1); 41 | 42 | const auto index = list.emplace(static_cast(0u), 43 | static_cast(0u), 44 | hfsm2::TransitionType::COUNT); 45 | REQUIRE(index == 0); 46 | REQUIRE(list.count() == CAPACITY); 47 | } 48 | 49 | AND_THEN("at the mid") { 50 | REQUIRE(list.count() == CAPACITY); 51 | 52 | constexpr List::Index mid = CAPACITY / 2; 53 | list.remove(mid); 54 | REQUIRE(list.count() == CAPACITY - 1); 55 | 56 | const auto index = list.emplace(static_cast(mid), 57 | static_cast(mid), 58 | hfsm2::TransitionType::COUNT); 59 | REQUIRE(index == mid); 60 | REQUIRE(list.count() == CAPACITY); 61 | } 62 | 63 | AND_THEN("at the end") { 64 | REQUIRE(list.count() == CAPACITY); 65 | 66 | constexpr List::Index end = CAPACITY - 1; 67 | list.remove(end); 68 | REQUIRE(list.count() == CAPACITY - 1); 69 | 70 | const auto index = list.emplace(static_cast(end), 71 | static_cast(end), 72 | hfsm2::TransitionType::COUNT); 73 | REQUIRE(index == end); 74 | REQUIRE(list.count() == CAPACITY); 75 | } 76 | } 77 | 78 | WHEN("fill, delete all and re-insert all elements") { 79 | for (List::Index i = 0; i < CAPACITY; ++i) { 80 | const auto index = list.emplace(static_cast(i), 81 | static_cast(i), 82 | hfsm2::TransitionType::COUNT); 83 | 84 | REQUIRE(index == i); 85 | REQUIRE(list.count() == i + 1); 86 | } 87 | 88 | for (List::Index i = 0; i < CAPACITY; ++i) 89 | REQUIRE(list[i] == hfsm2::detail::TaskBase{static_cast(i), 90 | static_cast(i), 91 | hfsm2::TransitionType::COUNT}); 92 | 93 | THEN("from the start") { 94 | REQUIRE(list.count() == CAPACITY); 95 | 96 | for (List::Index i = 0; i < CAPACITY; ++i) { 97 | list.remove(i); 98 | REQUIRE(list.count() == CAPACITY - 1 - i); 99 | } 100 | 101 | for (List::Index i = 0; i < CAPACITY; ++i) { 102 | const auto index = list.emplace(static_cast(i), 103 | static_cast(i), 104 | hfsm2::TransitionType::COUNT); 105 | 106 | REQUIRE(index == CAPACITY - 1 - i); 107 | REQUIRE(list.count() == i + 1); 108 | } 109 | } 110 | 111 | AND_THEN("at the mid") { 112 | REQUIRE(list.count() == CAPACITY); 113 | } 114 | 115 | AND_THEN("from the end") { 116 | REQUIRE(list.count() == CAPACITY); 117 | 118 | for (List::Index i = 0; i < CAPACITY; ++i) { 119 | list.remove(CAPACITY - 1 - i); 120 | REQUIRE(list.count() == CAPACITY - 1 - i); 121 | } 122 | 123 | for (List::Index i = 0; i < CAPACITY; ++i) { 124 | const auto index = list.emplace(static_cast(i), 125 | static_cast(i), 126 | hfsm2::TransitionType::COUNT); 127 | 128 | REQUIRE(index == i); 129 | REQUIRE(list.count() == i + 1); 130 | } 131 | } 132 | } 133 | } 134 | 135 | //////////////////////////////////////////////////////////////////////////////// 136 | 137 | } 138 | -------------------------------------------------------------------------------- /test/test_access.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_VERBOSE_DEBUG_LOG 5 | #include "tools.hpp" 6 | 7 | namespace test_access { 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | using M = hfsm2::Machine; 12 | 13 | //------------------------------------------------------------------------------ 14 | 15 | #define S(s) struct s 16 | 17 | using FSM = M::PeerRoot< 18 | M::Composite 24 | >, 25 | M::Orthogonal, 30 | M::Composite 34 | > 35 | >; 36 | 37 | #undef S 38 | 39 | //------------------------------------------------------------------------------ 40 | 41 | static_assert(FSM::regionId() == 1, ""); 42 | static_assert(FSM::regionId() == 2, ""); 43 | static_assert(FSM::regionId() == 3, ""); 44 | static_assert(FSM::regionId() == 4, ""); 45 | static_assert(FSM::regionId() == 5, ""); 46 | 47 | static_assert(FSM::stateId() == 1, ""); 48 | static_assert(FSM::stateId() == 2, ""); 49 | static_assert(FSM::stateId() == 3, ""); 50 | static_assert(FSM::stateId() == 4, ""); 51 | static_assert(FSM::stateId() == 5, ""); 52 | static_assert(FSM::stateId() == 6, ""); 53 | static_assert(FSM::stateId() == 7, ""); 54 | static_assert(FSM::stateId() == 8, ""); 55 | static_assert(FSM::stateId() == 9, ""); 56 | static_assert(FSM::stateId() == 10, ""); 57 | static_assert(FSM::stateId() == 11, ""); 58 | static_assert(FSM::stateId() == 12, ""); 59 | 60 | //////////////////////////////////////////////////////////////////////////////// 61 | 62 | struct A : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 63 | struct A_1 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 64 | struct A_2 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 65 | struct A_2_1 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 66 | struct A_2_2 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 67 | struct B : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 68 | struct B_1 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 69 | struct B_1_1 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 70 | struct B_1_2 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 71 | struct B_2 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 72 | struct B_2_1 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 73 | struct B_2_2 : FSM::State { const hfsm2::StateID id = FSM::stateId(); }; 74 | 75 | //////////////////////////////////////////////////////////////////////////////// 76 | 77 | static_assert(FSM::Instance::Info::STATE_COUNT == 13, ""); 78 | static_assert(FSM::Instance::Info::REGION_COUNT == 6, ""); 79 | static_assert(FSM::Instance::Info::COMPO_COUNT == 5, ""); 80 | static_assert(FSM::Instance::Info::COMPO_PRONGS == 10, ""); 81 | static_assert(FSM::Instance::Info::ORTHO_COUNT == 1, ""); 82 | static_assert(FSM::Instance::Info::ORTHO_UNITS == 1, ""); 83 | 84 | //////////////////////////////////////////////////////////////////////////////// 85 | 86 | TEST_CASE("FSM.Access") { 87 | FSM::Instance machine; 88 | 89 | REQUIRE(machine.access().id == FSM::stateId()); 90 | REQUIRE(machine.access().id == FSM::stateId()); 91 | REQUIRE(machine.access().id == FSM::stateId()); 92 | REQUIRE(machine.access().id == FSM::stateId()); 93 | REQUIRE(machine.access().id == FSM::stateId()); 94 | REQUIRE(machine.access().id == FSM::stateId()); 95 | REQUIRE(machine.access().id == FSM::stateId()); 96 | REQUIRE(machine.access().id == FSM::stateId()); 97 | REQUIRE(machine.access().id == FSM::stateId()); 98 | REQUIRE(machine.access().id == FSM::stateId()); 99 | REQUIRE(machine.access().id == FSM::stateId()); 100 | REQUIRE(machine.access().id == FSM::stateId()); 101 | } 102 | 103 | //////////////////////////////////////////////////////////////////////////////// 104 | 105 | } 106 | -------------------------------------------------------------------------------- /test/test_react_order.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_PLANS 5 | #include "tools.hpp" 6 | 7 | using namespace test_tools; 8 | 9 | namespace test_react_order { 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | using Config = hfsm2::Config 14 | ::BottomUpReactions; 15 | 16 | using M = hfsm2::MachineT; 17 | 18 | using Logger = LoggerT; 19 | 20 | struct Empty {}; 21 | 22 | //------------------------------------------------------------------------------ 23 | 24 | #define S(s) struct s 25 | 26 | using FSM = M::OrthogonalRoot, 31 | M::Composite 35 | >; 36 | 37 | #undef S 38 | 39 | //------------------------------------------------------------------------------ 40 | 41 | static_assert(FSM::stateId() == 0, ""); 42 | static_assert(FSM::stateId() == 1, ""); 43 | static_assert(FSM::stateId() == 2, ""); 44 | static_assert(FSM::stateId() == 3, ""); 45 | static_assert(FSM::stateId() == 4, ""); 46 | static_assert(FSM::stateId() == 5, ""); 47 | static_assert(FSM::stateId() == 6, ""); 48 | 49 | //////////////////////////////////////////////////////////////////////////////// 50 | 51 | struct R : FSM::State {}; 52 | struct C1 : FSM::State {}; 53 | struct I1 : FSM::State {}; 54 | 55 | struct D1 56 | : FSM::State 57 | { 58 | void preReact(const Empty&, EventControl& control) { 59 | control.consumeEvent(); 60 | } 61 | 62 | void react(const Empty&, EventControl& control) { 63 | control.consumeEvent(); 64 | } 65 | 66 | void postReact(const Empty&, EventControl& control) { 67 | control.consumeEvent(); 68 | } 69 | }; 70 | 71 | struct C2 : FSM::State {}; 72 | struct I2 : FSM::State {}; 73 | struct D2 : FSM::State {}; 74 | 75 | //////////////////////////////////////////////////////////////////////////////// 76 | 77 | TEST_CASE("FSM.Prepend Plans") { 78 | Logger logger; 79 | 80 | WHEN("Created") { 81 | FSM::Instance machine{&logger}; 82 | 83 | logger.assertSequence({}); 84 | 85 | assertActive(machine, { 86 | FSM::stateId(), 87 | FSM::stateId(), 88 | FSM::stateId(), 89 | FSM::stateId(), 90 | FSM::stateId(), 91 | }); 92 | 93 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 94 | 95 | THEN("Initial reactions") { 96 | machine.react(Empty{}); 97 | 98 | logger.assertSequence({ 99 | { FSM::stateId(), Event::Type::PRE_REACT }, 100 | { FSM::stateId(), Event::Type::PRE_REACT }, 101 | { FSM::stateId(), Event::Type::PRE_REACT }, 102 | { FSM::stateId(), Event::Type::PRE_REACT }, 103 | { FSM::stateId(), Event::Type::PRE_REACT }, 104 | 105 | { FSM::stateId(), Event::Type::REACT }, 106 | { FSM::stateId(), Event::Type::REACT }, 107 | { FSM::stateId(), Event::Type::REACT }, 108 | { FSM::stateId(), Event::Type::REACT }, 109 | { FSM::stateId(), Event::Type::REACT }, 110 | 111 | { FSM::stateId(), Event::Type::POST_REACT }, 112 | { FSM::stateId(), Event::Type::POST_REACT }, 113 | { FSM::stateId(), Event::Type::POST_REACT }, 114 | { FSM::stateId(), Event::Type::POST_REACT }, 115 | { FSM::stateId(), Event::Type::POST_REACT }, 116 | }); 117 | } 118 | 119 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 120 | 121 | AND_THEN("Secondary reactions") { 122 | machine. changeTo(); 123 | machine.immediateChangeTo(); 124 | 125 | assertActive(machine, { 126 | FSM::stateId(), 127 | FSM::stateId(), 128 | FSM::stateId(), 129 | FSM::stateId(), 130 | FSM::stateId(), 131 | }); 132 | 133 | machine.react(Empty{}); 134 | 135 | logger.assertSequence({ 136 | { Event::Type::CHANGE, FSM::stateId() }, 137 | { Event::Type::CHANGE, FSM::stateId() }, 138 | 139 | { FSM::stateId(), Event::Type::PRE_REACT }, 140 | 141 | { FSM::stateId(), Event::Type::REACT }, 142 | 143 | { FSM::stateId(), Event::Type::POST_REACT }, 144 | { FSM::stateId(), Event::Type::POST_REACT }, 145 | { FSM::stateId(), Event::Type::POST_REACT }, 146 | }); 147 | } 148 | 149 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 150 | } 151 | } 152 | 153 | //////////////////////////////////////////////////////////////////////////////// 154 | 155 | } 156 | -------------------------------------------------------------------------------- /development/hfsm2/detail/structure/ancestors_2.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | struct HFSM2_EMPTY_BASES A_ 8 | : TFirst 9 | { 10 | using First = TFirst; 11 | using typename First::Context; 12 | 13 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 14 | using typename First::Rank; 15 | using typename First::Utility; 16 | #endif 17 | 18 | using typename First::StateList; 19 | using typename First::RegionList; 20 | 21 | using typename First::Payload; 22 | using typename First::Transition; 23 | 24 | using typename First::ConstControl; 25 | using typename First::Control; 26 | using typename First::PlanControl; 27 | 28 | #if HFSM2_PLANS_AVAILABLE() 29 | using typename First::Plan; 30 | #endif 31 | 32 | using typename First::FullControl; 33 | using typename First::GuardControl; 34 | using typename First::EventControl; 35 | 36 | using First::stateId; 37 | using First::regionId; 38 | 39 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 40 | 41 | HFSM2_CONSTEXPR(14) Prong select (const Control& ) noexcept { return 0; } 42 | 43 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 44 | HFSM2_CONSTEXPR(14) Rank rank (const Control& ) noexcept { return Rank {0}; } 45 | HFSM2_CONSTEXPR(14) Utility utility (const Control& ) noexcept { return Utility{1}; } 46 | #endif 47 | 48 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 49 | 50 | HFSM2_CONSTEXPR(14) void entryGuard (GuardControl& ) noexcept {} 51 | 52 | HFSM2_CONSTEXPR(14) void enter ( PlanControl& ) noexcept {} 53 | HFSM2_CONSTEXPR(14) void reenter ( PlanControl& ) noexcept {} 54 | 55 | HFSM2_CONSTEXPR(14) void preUpdate ( FullControl& ) noexcept {} 56 | HFSM2_CONSTEXPR(14) void update ( FullControl& ) noexcept {} 57 | HFSM2_CONSTEXPR(14) void postUpdate ( FullControl& ) noexcept {} 58 | 59 | template 60 | HFSM2_CONSTEXPR(14) void preReact (const TEvent& , 61 | EventControl& ) noexcept {} 62 | 63 | template 64 | HFSM2_CONSTEXPR(14) void react (const TEvent& , 65 | EventControl& ) noexcept {} 66 | 67 | template 68 | HFSM2_CONSTEXPR(14) void postReact (const TEvent& , 69 | EventControl& ) noexcept {} 70 | 71 | template 72 | HFSM2_CONSTEXPR(14) void query ( TEvent& , 73 | ConstControl& ) const noexcept {} 74 | 75 | HFSM2_CONSTEXPR(14) void exitGuard (GuardControl& ) noexcept {} 76 | 77 | HFSM2_CONSTEXPR(14) void exit ( PlanControl& ) noexcept {} 78 | 79 | #if HFSM2_PLANS_AVAILABLE() 80 | HFSM2_CONSTEXPR(14) void planSucceeded ( FullControl& control) noexcept; 81 | HFSM2_CONSTEXPR(14) void planFailed ( FullControl& control) noexcept; 82 | #endif 83 | 84 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 85 | 86 | HFSM2_CONSTEXPR(14) void wideEntryGuard(GuardControl& control) noexcept; 87 | 88 | HFSM2_CONSTEXPR(14) void wideEnter ( PlanControl& control) noexcept; 89 | HFSM2_CONSTEXPR(14) void wideReenter ( PlanControl& control) noexcept; 90 | 91 | HFSM2_CONSTEXPR(14) void widePreUpdate ( FullControl& control) noexcept; 92 | HFSM2_CONSTEXPR(14) void wideUpdate ( FullControl& control) noexcept; 93 | HFSM2_CONSTEXPR(14) void widePostUpdate( FullControl& control) noexcept; 94 | 95 | template 96 | HFSM2_CONSTEXPR(14) void widePreReact (const TEvent& event , 97 | EventControl& control) noexcept; 98 | 99 | template 100 | HFSM2_CONSTEXPR(14) void wideReact (const TEvent& event , 101 | EventControl& control) noexcept; 102 | 103 | template 104 | HFSM2_CONSTEXPR(14) void widePostReact (const TEvent& event , 105 | EventControl& control) noexcept; 106 | 107 | template 108 | HFSM2_CONSTEXPR(14) void wideQuery ( TEvent& event , 109 | ConstControl& control) const noexcept; 110 | 111 | HFSM2_CONSTEXPR(14) void wideExitGuard (GuardControl& control) noexcept; 112 | 113 | HFSM2_CONSTEXPR(14) void wideExit ( PlanControl& control) noexcept; 114 | 115 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 116 | }; 117 | 118 | //////////////////////////////////////////////////////////////////////////////// 119 | 120 | } 121 | } 122 | 123 | #include "ancestors_2.inl" 124 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/control_4.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | class GuardControlT final 8 | : public FullControlT 9 | { 10 | template 11 | friend struct S_; 12 | 13 | template 14 | friend class R_; 15 | 16 | using FullControl = FullControlT; 17 | 18 | using typename FullControl::Core; 19 | using typename FullControl::Context; 20 | 21 | using typename FullControl::Registry; 22 | using typename FullControl::TransitionSet; 23 | using typename FullControl::TransitionSets; 24 | 25 | #if HFSM2_TRANSITION_HISTORY_AVAILABLE() 26 | using typename FullControl::TransitionTargets; 27 | #endif 28 | 29 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 30 | using typename FullControl::RNG; 31 | #endif 32 | 33 | #if HFSM2_PLANS_AVAILABLE() 34 | using typename FullControl::PlanData; 35 | #endif 36 | 37 | #if HFSM2_LOG_INTERFACE_AVAILABLE() 38 | using typename FullControl::Logger; 39 | #endif 40 | 41 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 42 | 43 | HFSM2_CONSTEXPR(11) GuardControlT(Core& core, 44 | const TransitionSets& currentTransitions, 45 | const TransitionSet& pendingTransitions) noexcept 46 | : FullControl{core, currentTransitions} 47 | , _pendingTransitions{pendingTransitions} 48 | {} 49 | 50 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 51 | 52 | public: 53 | using FullControl::context; 54 | 55 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 56 | 57 | /// @brief Check if a state is going to be activated or deactivated 58 | /// @param `stateId` State identifier 59 | /// @return State pending activation/deactivation status 60 | HFSM2_CONSTEXPR(11) bool isPendingChange(const StateID stateId_) const noexcept { return _core.registry.isPendingChange(stateId_); } 61 | 62 | /// @brief Check if a state is going to be activated or deactivated 63 | /// @tparam `TState` State type 64 | /// @return State pending activation/deactivation status 65 | template 66 | HFSM2_CONSTEXPR(11) bool isPendingChange() const noexcept { return isPendingChange(FullControl::template stateId()); } 67 | 68 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 69 | 70 | /// @brief Check if a state is going to be activated 71 | /// @param `stateId` State identifier 72 | /// @return State pending activation status 73 | HFSM2_CONSTEXPR(11) bool isPendingEnter (const StateID stateId_) const noexcept { return _core.registry.isPendingEnter (stateId_); } 74 | 75 | /// @brief Check if a state is going to be activated 76 | /// @tparam `TState` State type 77 | /// @return State pending activation status 78 | template 79 | HFSM2_CONSTEXPR(11) bool isPendingEnter () const noexcept { return isPendingEnter (FullControl::template stateId()); } 80 | 81 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 82 | 83 | /// @brief Check if a state is going to be deactivated 84 | /// @param `stateId` State identifier 85 | /// @return State pending deactivation status 86 | HFSM2_CONSTEXPR(11) bool isPendingExit (const StateID stateId_) const noexcept { return _core.registry.isPendingExit (stateId_); } 87 | 88 | /// @brief Check if a state is going to be deactivated 89 | /// @tparam `TState` State type 90 | /// @return State pending deactivation status 91 | template 92 | HFSM2_CONSTEXPR(11) bool isPendingExit () const noexcept { return isPendingExit (FullControl::template stateId()); } 93 | 94 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 95 | // COMMON 96 | 97 | /// @brief Get pending transition requests 98 | /// @return Array of pending transition requests 99 | HFSM2_CONSTEXPR(11) const TransitionSet& pendingTransitions() const noexcept { return _pendingTransitions; } 100 | 101 | /// @brief Cancel pending transition requests 102 | /// (can be used to substitute a transition into the current state with a different one) 103 | HFSM2_CONSTEXPR(14) void cancelPendingTransitions() noexcept; 104 | 105 | private: 106 | using FullControl::_core; 107 | using FullControl::_originId; 108 | 109 | const TransitionSet& _pendingTransitions; 110 | 111 | bool _cancelled = false; 112 | }; 113 | 114 | //////////////////////////////////////////////////////////////////////////////// 115 | 116 | } 117 | } 118 | 119 | #include "control_4.inl" 120 | -------------------------------------------------------------------------------- /examples/basic_audio_player/main.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | // 4 | // An HFSM2 port of https://gist.github.com/martinmoene/797b1923f9c6c1ae355bb2d6870be25e 5 | // by Martin Moene (see https://twitter.com/MartinMoene/status/1118453128834232320) 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | #define HFSM2_ENABLE_LOG_INTERFACE 13 | #define HFSM2_ENABLE_STRUCTURE_REPORT 14 | #include 15 | 16 | //------------------------------------------------------------------------------ 17 | 18 | // events 19 | struct Play { std::string title; }; 20 | struct Pause {}; 21 | struct Resume {}; 22 | struct Stop {}; 23 | 24 | // shared data stored externally to the fsm 25 | using Context = std::string; 26 | using M = hfsm2::MachineT>; 27 | 28 | #if 0 29 | 30 | // states need to be forward declared to be used in FSM struct declaration 31 | struct Idle; 32 | struct Playing; 33 | struct Paused; 34 | 35 | using FSM = M::PeerRoot< 36 | Idle, 37 | Playing, 38 | Paused 39 | >; 40 | 41 | #else 42 | 43 | // alternatively, some macro magic can be invoked to simplify FSM structure declaration 44 | #define S(s) struct s 45 | 46 | using FSM = M::PeerRoot< 47 | S(Idle), 48 | S(Playing), 49 | S(Paused) 50 | >; 51 | 52 | #undef S 53 | 54 | #endif 55 | 56 | //------------------------------------------------------------------------------ 57 | 58 | // double-check state ids for Logger::stateName() 59 | static_assert(FSM::stateId() == 1, ""); 60 | static_assert(FSM::stateId() == 2, ""); 61 | static_assert(FSM::stateId() == 3, ""); 62 | 63 | // custom logger for recording all transitions 64 | struct Logger 65 | : M::LoggerInterface 66 | { 67 | static const char* stateName(const StateID stateId) { 68 | switch (stateId) { 69 | case 1: 70 | return "Idle"; 71 | case 2: 72 | return "Playing"; 73 | case 3: 74 | return "Paused"; 75 | default: 76 | assert(false); 77 | return ""; 78 | } 79 | } 80 | 81 | void recordTransition(const Context& /*context*/, 82 | const StateID origin, 83 | const TransitionType /*transition*/, 84 | const StateID target) override 85 | { 86 | std::cout << stateName(origin) << " -> " << stateName(target) << "\n"; 87 | } 88 | }; 89 | 90 | //------------------------------------------------------------------------------ 91 | 92 | // state helper for error reporting 93 | struct Base 94 | : FSM::State 95 | { 96 | template 97 | void react(const Event&, EventControl&) { 98 | std::cout << "[unsupported transition]\n"; 99 | } 100 | }; 101 | 102 | // states 103 | struct Idle 104 | : Base 105 | { 106 | using Base::react; 107 | 108 | void react(const Play& event, EventControl& control) { 109 | control.context() = event.title; 110 | control.changeTo(); 111 | } 112 | }; 113 | 114 | struct Playing 115 | : Base 116 | { 117 | using Base::react; 118 | 119 | void react(const Pause&, EventControl& control) { 120 | control.changeTo(); 121 | } 122 | 123 | void react(const Stop&, EventControl& control) { 124 | control.changeTo(); 125 | } 126 | }; 127 | 128 | struct Paused 129 | : Base 130 | { 131 | using Base::react; 132 | 133 | void react(const Resume&, EventControl& control) { 134 | control.changeTo(); 135 | } 136 | 137 | void react(const Stop&, EventControl& control) { 138 | control.changeTo(); 139 | } 140 | }; 141 | 142 | //------------------------------------------------------------------------------ 143 | 144 | int main() { 145 | // construct everything 146 | Context title; 147 | Logger logger; 148 | FSM::Instance machine(title, &logger); 149 | 150 | // do the work :) 151 | machine.react(Play{"any"}); // Idle -> Playing 152 | machine.react(Stop{}); // Playing -> Idle 153 | 154 | machine.react(Play{"optional"}); // Idle -> Playing 155 | machine.react(Pause{}); // Playing -> Paused 156 | machine.react(Stop{}); // Paused -> Idle 157 | 158 | machine.react(Play{"variant"}); // Idle -> Playing 159 | machine.react(Pause{}); // Playing -> Paused 160 | machine.react(Resume{}); // Paused -> Playing 161 | machine.react(Stop{}); // Playing -> Idle 162 | 163 | machine.react(Pause{}); // [unsupported transition] 164 | machine.react(Resume{}); // [unsupported transition] 165 | machine.react(Stop{}); // [unsupported transition] 166 | 167 | return 0; 168 | } 169 | 170 | //------------------------------------------------------------------------------ 171 | -------------------------------------------------------------------------------- /development/hfsm2/detail/containers/bit_array.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | struct Units final { 7 | HFSM2_CONSTEXPR(11) Units(Short unit_ = INVALID_SHORT, 8 | Short width_ = INVALID_SHORT) noexcept 9 | : unit {unit_ } 10 | , width{width_} 11 | {} 12 | 13 | Short unit; 14 | Short width; 15 | }; 16 | 17 | //------------------------------------------------------------------------------ 18 | 19 | template 20 | class BitArrayT final { 21 | public: 22 | using Index = UCapacity; 23 | 24 | static constexpr Index CAPACITY = NCapacity; 25 | static constexpr Index UNIT_COUNT = contain(CAPACITY, 8); 26 | 27 | using BitArray = BitArrayT; 28 | 29 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 30 | 31 | class Bits { 32 | template 33 | friend class BitArrayT; 34 | 35 | private: 36 | HFSM2_CONSTEXPR(11) explicit Bits(uint8_t* const storage, 37 | const Index width) noexcept 38 | : _storage{storage} 39 | , _width{width} 40 | {} 41 | 42 | public: 43 | HFSM2_CONSTEXPR(14) explicit operator bool() const noexcept; 44 | 45 | HFSM2_CONSTEXPR(14) void clear() noexcept; 46 | 47 | template 48 | HFSM2_CONSTEXPR(14) bool get() const noexcept; 49 | 50 | template 51 | HFSM2_CONSTEXPR(14) void set() noexcept; 52 | 53 | template 54 | HFSM2_CONSTEXPR(14) void clear() noexcept; 55 | 56 | HFSM2_CONSTEXPR(14) bool get (const Index index) const noexcept; 57 | HFSM2_CONSTEXPR(14) void set (const Index index) noexcept; 58 | HFSM2_CONSTEXPR(14) void clear(const Index index) noexcept; 59 | 60 | private: 61 | uint8_t* const _storage; 62 | const Index _width; 63 | }; 64 | 65 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 66 | 67 | class CBits { 68 | template 69 | friend class BitArrayT; 70 | 71 | private: 72 | HFSM2_CONSTEXPR(11) explicit CBits(const uint8_t* const storage, 73 | const Index width) noexcept 74 | : _storage{storage} 75 | , _width{width} 76 | {} 77 | 78 | public: 79 | HFSM2_CONSTEXPR(14) explicit operator bool() const noexcept; 80 | 81 | template 82 | HFSM2_CONSTEXPR(14) bool get() const noexcept; 83 | 84 | HFSM2_CONSTEXPR(14) bool get(const Index index) const noexcept; 85 | 86 | private: 87 | const uint8_t* const _storage; 88 | const Index _width; 89 | }; 90 | 91 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 92 | 93 | public: 94 | HFSM2_CONSTEXPR(14) BitArrayT() noexcept { clear(); } 95 | 96 | HFSM2_CONSTEXPR(14) void set() noexcept; 97 | 98 | HFSM2_CONSTEXPR(14) void clear() noexcept; 99 | 100 | template 101 | HFSM2_CONSTEXPR(14) bool get() const noexcept; 102 | 103 | template 104 | HFSM2_CONSTEXPR(14) void set() noexcept; 105 | 106 | template 107 | HFSM2_CONSTEXPR(14) void clear() noexcept; 108 | 109 | HFSM2_CONSTEXPR(14) bool empty() const noexcept; 110 | 111 | template 112 | HFSM2_CONSTEXPR(14) bool get (const TIndex index) const noexcept; 113 | 114 | template 115 | HFSM2_CONSTEXPR(14) void set (const TIndex index) noexcept; 116 | 117 | template 118 | HFSM2_CONSTEXPR(14) void clear(const TIndex index) noexcept; 119 | 120 | HFSM2_CONSTEXPR(14) bool operator != (const BitArray& other) const noexcept; 121 | 122 | HFSM2_CONSTEXPR(14) bool operator & (const BitArray& other) const noexcept; 123 | 124 | HFSM2_CONSTEXPR(14) void operator &= (const BitArray& other) noexcept; 125 | 126 | template 127 | HFSM2_CONSTEXPR(14) Bits bits() noexcept; 128 | 129 | template 130 | HFSM2_CONSTEXPR(14) CBits cbits() const noexcept; 131 | 132 | HFSM2_CONSTEXPR(14) Bits bits(const Units& units) noexcept; 133 | HFSM2_CONSTEXPR(14) CBits cbits(const Units& units) const noexcept; 134 | 135 | private: 136 | uint8_t _storage[UNIT_COUNT] {}; 137 | }; 138 | 139 | //------------------------------------------------------------------------------ 140 | 141 | template <> 142 | class BitArrayT<0> final { 143 | public: 144 | HFSM2_CONSTEXPR(14) void clear() noexcept {} 145 | 146 | HFSM2_CONSTEXPR(11) bool empty() const noexcept { return true; } 147 | }; 148 | 149 | //////////////////////////////////////////////////////////////////////////////// 150 | 151 | } 152 | } 153 | 154 | #include "bit_array.inl" 155 | -------------------------------------------------------------------------------- /development/hfsm2/detail/shared/macros_off.hpp: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | 3 | #if defined(__GNUC__) || defined(__GNUG__) 4 | #pragma GCC diagnostic pop 5 | #endif 6 | 7 | #ifdef __clang__ 8 | #pragma clang diagnostic pop 9 | #endif 10 | 11 | #if _MSC_VER == 1900 12 | #pragma warning(pop) 13 | #endif 14 | 15 | //////////////////////////////////////////////////////////////////////////////// 16 | 17 | #undef HFSM2_UNUSED 18 | 19 | //------------------------------------------------------------------------------ 20 | 21 | #undef HFSM2_ATTRIBUTE 22 | 23 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 24 | 25 | #undef HFSM2_ATTRIBUTE_FALLTHROUGH 26 | 27 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 28 | 29 | #undef HFSM2_ATTRIBUTE_NO_UNIQUE_ADDRESS 30 | 31 | //------------------------------------------------------------------------------ 32 | 33 | #undef HFSM2_CONSTEXPR 34 | 35 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 | 37 | #undef HFSM2_CONSTEXPR_NO 38 | 39 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 40 | 41 | #undef HFSM2_CONSTEXPR_11 42 | 43 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 44 | 45 | #undef HFSM2_CONSTEXPR_14 46 | 47 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 48 | 49 | #undef HFSM2_CONSTEXPR_17 50 | 51 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 52 | 53 | #undef HFSM2_CONSTEXPR_20 54 | 55 | //------------------------------------------------------------------------------ 56 | 57 | //#undef HFSM2_EMPTY_BASES 58 | 59 | //------------------------------------------------------------------------------ 60 | 61 | //#undef HFSM2_ARCHITECTURE 62 | //#undef HFSM2_ARCHITECTURE_64 63 | //#undef HFSM2_ARCHITECTURE_32 64 | #undef HFSM2_64BIT_OR_32BIT 65 | 66 | //------------------------------------------------------------------------------ 67 | 68 | //#undef HFSM2_BREAK 69 | #undef HFSM2_BREAK_AVAILABLE 70 | 71 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 72 | 73 | #undef HFSM2_IF_DEBUG 74 | #undef HFSM2_UNLESS_DEBUG 75 | #undef HFSM2_DEBUG_OR 76 | 77 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 78 | 79 | //#undef HFSM2_ASSERT_AVAILABLE 80 | #undef HFSM2_IF_ASSERT 81 | //#undef HFSM2_CHECKED 82 | #undef HFSM2_ASSERT 83 | #undef HFSM2_ASSERT_OR 84 | 85 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 86 | 87 | #undef HFSM2_EXPLICIT_MEMBER_SPECIALIZATION_AVAILABLE 88 | 89 | //////////////////////////////////////////////////////////////////////////////// 90 | //------------------------------------------------------------------------------ 91 | 92 | #undef HFSM2_IF_TYPEINDEX 93 | #undef HFSM2_TYPEINDEX_AVAILABLE 94 | #undef HFSM2_IF_TYPEINDEX 95 | 96 | //------------------------------------------------------------------------------ 97 | 98 | //#undef HFSM2_DEBUG_STATE_TYPE_AVAILABLE 99 | 100 | //------------------------------------------------------------------------------ 101 | 102 | #undef HFSM2_PLANS_AVAILABLE 103 | #undef HFSM2_IF_PLANS 104 | 105 | //------------------------------------------------------------------------------ 106 | 107 | #undef HFSM2_SERIALIZATION_AVAILABLE 108 | #undef HFSM2_IF_SERIALIZATION 109 | 110 | //------------------------------------------------------------------------------ 111 | 112 | #undef HFSM2_STRUCTURE_REPORT_AVAILABLE 113 | #undef HFSM2_IF_STRUCTURE_REPORT 114 | 115 | //------------------------------------------------------------------------------ 116 | 117 | #undef HFSM2_TRANSITION_HISTORY_AVAILABLE 118 | #undef HFSM2_IF_TRANSITION_HISTORY 119 | 120 | //------------------------------------------------------------------------------ 121 | 122 | #undef HFSM2_UTILITY_THEORY_AVAILABLE 123 | #undef HFSM2_IF_UTILITY_THEORY 124 | 125 | //////////////////////////////////////////////////////////////////////////////// 126 | 127 | #undef HFSM2_VERBOSE_DEBUG_LOG_AVAILABLE 128 | 129 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 130 | 131 | #undef HFSM2_LOG_INTERFACE_AVAILABLE 132 | #undef HFSM2_IF_LOG_INTERFACE 133 | 134 | #undef HFSM2_LOG_TRANSITION 135 | 136 | #ifdef HFSM2_ENABLE_PLANS 137 | #undef HFSM2_LOG_TASK_STATUS 138 | #undef HFSM2_LOG_PLAN_STATUS 139 | #endif 140 | 141 | #undef HFSM2_LOG_CANCELLED_PENDING 142 | 143 | #ifdef HFSM2_UTILITY_THEORY_AVAILABLE 144 | #undef HFSM2_LOG_UTILITY_RESOLUTION 145 | #undef HFSM2_LOG_RANDOM_RESOLUTION 146 | #endif 147 | 148 | #undef HFSM2_LOG_STATE_METHOD 149 | 150 | //////////////////////////////////////////////////////////////////////////////// 151 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root/control_2.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | class PlanControlT 8 | : public ControlT 9 | { 10 | template 11 | friend struct S_; 12 | 13 | template 14 | friend struct C_; 15 | 16 | template 17 | friend struct O_; 18 | 19 | template 20 | friend class R_; 21 | 22 | template 23 | friend class RV_; 24 | 25 | protected: 26 | using Control = ControlT; 27 | 28 | using typename Control::StateList; 29 | using typename Control::RegionList; 30 | 31 | using typename Control::Core; 32 | 33 | using TransitionSets = typename Core::TransitionSets; 34 | 35 | #if HFSM2_PLANS_AVAILABLE() 36 | using typename Control::PlanData; 37 | #endif 38 | 39 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 40 | 41 | struct Region { 42 | HFSM2_CONSTEXPR(14) Region(PlanControlT& control, 43 | const RegionID regionId_, 44 | const StateID index, 45 | const Long size) noexcept; 46 | 47 | HFSM2_CONSTEXPR(20) ~Region() noexcept; 48 | 49 | PlanControlT& control; 50 | const RegionID prevId; 51 | const Long prevIndex; 52 | const Long prevSize; 53 | }; 54 | 55 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 56 | 57 | HFSM2_CONSTEXPR(11) PlanControlT(Core& core, 58 | const TransitionSets& currentTransitions) noexcept 59 | : Control{core} 60 | , _currentTransitions{currentTransitions} 61 | {} 62 | 63 | HFSM2_CONSTEXPR(14) void setRegion(const RegionID regionId_, 64 | const StateID index, 65 | const Long size) noexcept; 66 | 67 | HFSM2_CONSTEXPR(14) void resetRegion(const RegionID regionId_, 68 | const StateID index, 69 | const Long size) noexcept; 70 | 71 | public: 72 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 73 | 74 | #if HFSM2_PLANS_AVAILABLE() 75 | using typename Control::CPlan; 76 | 77 | using Plan = PayloadPlanT; 78 | 79 | /// @brief Access plan for the current region 80 | /// @return Plan for the current region 81 | /// @see `HFSM2_ENABLE_PLANS` 82 | HFSM2_CONSTEXPR(14) Plan plan() noexcept { return Plan{_core.registry, _core.planData, _regionId }; } 83 | 84 | // COMMON 85 | 86 | /// @brief Access plan for a region 87 | /// @param `regionId` Region identifier 88 | /// @return Plan for the region 89 | /// @see `HFSM2_ENABLE_PLANS` 90 | HFSM2_CONSTEXPR(14) Plan plan(const RegionID regionId_) noexcept { return Plan{_core.registry, _core.planData, regionId_ }; } 91 | 92 | /// @brief Access plan for a region 93 | /// @tparam `TRegion` Region head state type 94 | /// @return Plan for the region 95 | /// @see `HFSM2_ENABLE_PLANS` 96 | template 97 | HFSM2_CONSTEXPR(14) Plan plan() noexcept { return Plan{_core.registry, _core.planData, Control::template regionId()}; } 98 | 99 | // COMMON 100 | 101 | /// @brief Access plan for the current region 102 | /// @return Plan for the current region 103 | /// @see `HFSM2_ENABLE_PLANS` 104 | HFSM2_CONSTEXPR(11) CPlan plan() const noexcept { return CPlan{_core.registry, _core.planData, _regionId }; } 105 | 106 | // COMMON 107 | 108 | /// @brief Access plan for a region 109 | /// @param `regionId` 110 | /// @return Plan for the region 111 | /// @see `HFSM2_ENABLE_PLANS` 112 | HFSM2_CONSTEXPR(11) CPlan plan(const RegionID regionId_) const noexcept { return CPlan{_core.registry, _core.planData, regionId_ }; } 113 | 114 | /// @brief Access plan for a region 115 | /// @tparam `TRegion` Region head state type 116 | /// @return Plan for the region 117 | /// @see `HFSM2_ENABLE_PLANS` 118 | template 119 | HFSM2_CONSTEXPR(11) CPlan plan() const noexcept { return CPlan{_core.registry, _core.planData, Control::template regionId()}; } 120 | 121 | #endif 122 | 123 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 124 | 125 | /// @brief Get current transition requests 126 | /// @return DynamicArrayT of pending transition requests 127 | HFSM2_CONSTEXPR(11) const TransitionSets& currentTransitions() const noexcept { return _currentTransitions; } 128 | 129 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 130 | 131 | protected: 132 | using Control::_core; 133 | using Control::_regionId; 134 | 135 | const TransitionSets& _currentTransitions; 136 | 137 | StateID _regionStateId = 0; 138 | Long _regionSize = StateList::SIZE; 139 | TaskStatus _taskStatus; 140 | }; 141 | 142 | //////////////////////////////////////////////////////////////////////////////// 143 | 144 | } 145 | } 146 | 147 | #include "control_2.inl" 148 | -------------------------------------------------------------------------------- /development/hfsm2/detail/features/task_list.inl: -------------------------------------------------------------------------------- 1 | #if HFSM2_PLANS_AVAILABLE() 2 | 3 | namespace hfsm2 { 4 | namespace detail { 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | template 9 | HFSM2_CONSTEXPR(14) 10 | void 11 | TaskListT::clear() noexcept { 12 | _vacantHead = 0; 13 | _vacantTail = 0; 14 | _last = 0; 15 | _count = 0; 16 | } 17 | 18 | //------------------------------------------------------------------------------ 19 | 20 | template 21 | template 22 | HFSM2_CONSTEXPR(14) 23 | Long 24 | TaskListT::emplace(TA_&&... args) noexcept { 25 | HFSM2_ASSERT(_last <= CAPACITY); 26 | 27 | if (_count < CAPACITY) { 28 | HFSM2_ASSERT(_vacantHead < CAPACITY); 29 | HFSM2_ASSERT(_vacantTail < CAPACITY); 30 | 31 | const Index index = _vacantHead; 32 | Item& item = _items[index]; 33 | 34 | if (_vacantHead != _vacantTail) { 35 | // recycle 36 | HFSM2_ASSERT(item.prev == INVALID); 37 | HFSM2_ASSERT(item.next != INVALID); 38 | 39 | _vacantHead = item.next; 40 | 41 | Item& head = _items[_vacantHead]; 42 | HFSM2_ASSERT(head.prev == index); 43 | head.prev = INVALID; 44 | } else if (_last < CAPACITY - 1) { 45 | // grow 46 | ++_last; 47 | _vacantHead = _last; 48 | _vacantTail = _last; 49 | 50 | Item& vacant = _items[_vacantHead]; 51 | vacant.prev = INVALID; 52 | vacant.next = INVALID; 53 | } else { 54 | // last 55 | HFSM2_ASSERT(_count == CAPACITY - 1); 56 | 57 | _last = CAPACITY; 58 | _vacantHead = INVALID; 59 | _vacantTail = INVALID; 60 | } 61 | 62 | new (&item) Item{::hfsm2::forward(args)...}; 63 | ++_count; 64 | 65 | HFSM2_IF_ASSERT(verifyStructure()); 66 | 67 | return index; 68 | } else { 69 | // full 70 | HFSM2_ASSERT(_vacantHead == INVALID); 71 | HFSM2_ASSERT(_vacantTail == INVALID); 72 | HFSM2_ASSERT(_count == CAPACITY); 73 | HFSM2_BREAK(); 74 | 75 | return INVALID; 76 | } 77 | } 78 | 79 | //------------------------------------------------------------------------------ 80 | 81 | template 82 | HFSM2_CONSTEXPR(14) 83 | void 84 | TaskListT::remove(const Index i) noexcept { 85 | HFSM2_ASSERT(i < CAPACITY && _count); 86 | 87 | Item& item = _items[i]; 88 | 89 | if (_count < CAPACITY) { 90 | HFSM2_ASSERT(_vacantHead < CAPACITY); 91 | HFSM2_ASSERT(_vacantTail < CAPACITY); 92 | 93 | item.prev = INVALID; 94 | item.next = _vacantHead; 95 | 96 | Item& head = _items[_vacantHead]; 97 | head.prev = i; 98 | 99 | _vacantHead = i; 100 | } else { 101 | // 0 -> 1 102 | HFSM2_ASSERT(_count == CAPACITY); 103 | HFSM2_ASSERT(_vacantHead == INVALID); 104 | HFSM2_ASSERT(_vacantTail == INVALID); 105 | 106 | item.prev = INVALID; 107 | item.next = INVALID; 108 | 109 | _vacantHead = i; 110 | _vacantTail = i; 111 | } 112 | 113 | --_count; 114 | 115 | HFSM2_IF_ASSERT(verifyStructure()); 116 | } 117 | 118 | //------------------------------------------------------------------------------ 119 | 120 | template 121 | HFSM2_CONSTEXPR(14) 122 | typename TaskListT::Item& 123 | TaskListT::operator[] (const Index i) noexcept { 124 | HFSM2_IF_ASSERT(verifyStructure()); 125 | 126 | return _items[i]; 127 | } 128 | 129 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 130 | 131 | template 132 | HFSM2_CONSTEXPR(11) 133 | const typename TaskListT::Item& 134 | TaskListT::operator[] (const Index i) const noexcept { 135 | HFSM2_IF_ASSERT(verifyStructure()); 136 | 137 | return _items[i]; 138 | } 139 | 140 | //------------------------------------------------------------------------------ 141 | 142 | #if HFSM2_ASSERT_AVAILABLE() 143 | 144 | template 145 | void 146 | TaskListT::verifyStructure(const Index occupied) const noexcept { 147 | if (_count < CAPACITY) { 148 | HFSM2_ASSERT(_vacantHead < CAPACITY); 149 | HFSM2_ASSERT(_vacantTail < CAPACITY); 150 | 151 | HFSM2_ASSERT(_items[_vacantHead].prev == INVALID); 152 | HFSM2_ASSERT(_items[_vacantTail].next == INVALID); 153 | 154 | Index emptyCount = 1; 155 | 156 | for (Index c = _vacantHead; c != _vacantTail; ) { 157 | HFSM2_ASSERT(occupied != c); 158 | 159 | const Item& current = _items[c]; 160 | 161 | const Long f = current.next; 162 | if (f != INVALID) { 163 | // next 164 | const Item& following = _items[f]; 165 | 166 | HFSM2_ASSERT(following.prev == c); 167 | 168 | c = f; 169 | continue; 170 | } else { 171 | // end 172 | HFSM2_ASSERT(_vacantTail == c); 173 | HFSM2_ASSERT(_count == CAPACITY - emptyCount); 174 | 175 | break; 176 | } 177 | } 178 | } else { 179 | HFSM2_ASSERT(_vacantHead == INVALID); 180 | HFSM2_ASSERT(_vacantTail == INVALID); 181 | } 182 | } 183 | 184 | #endif 185 | 186 | //////////////////////////////////////////////////////////////////////////////// 187 | 188 | } 189 | } 190 | 191 | #endif 192 | -------------------------------------------------------------------------------- /development/hfsm2/detail/root_4.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm2 { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | /// @brief FSM Root 7 | /// @tparam `TConfig` Type configuration 8 | /// @tparam `TApex` Root region type 9 | template < 10 | typename TConfig 11 | , typename TApex 12 | > 13 | class HFSM2_EMPTY_BASES InstanceT final 14 | : public RC_ 15 | { 16 | using Base = RC_; 17 | 18 | public: 19 | using Base::Base; 20 | }; 21 | 22 | #if HFSM2_UTILITY_THEORY_AVAILABLE() 23 | 24 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 25 | // TRNG == RNGT 26 | 27 | /// @brief FSM Root 28 | /// @tparam `TConfig` Type configuration 29 | /// @tparam `TApex` Root region type 30 | template < 31 | FeatureTag NFeatureTag 32 | , typename TContext 33 | , typename TActivation 34 | , typename TReactOrder 35 | , typename TRank 36 | , typename TUtility 37 | , Short NSubstitutionLimit 38 | HFSM2_IF_PLANS(, Long NTaskCapacity) 39 | , typename TPayload 40 | , typename TApex 41 | > 42 | class HFSM2_EMPTY_BASES InstanceT< 43 | G_< 44 | NFeatureTag 45 | , TContext 46 | , TActivation 47 | , TReactOrder 48 | , TRank 49 | , TUtility 50 | , RNGT 51 | , NSubstitutionLimit 52 | HFSM2_IF_PLANS(, NTaskCapacity) 53 | , TPayload 54 | > 55 | , TApex 56 | > final 57 | : public RC_< 58 | G_< 59 | NFeatureTag 60 | , TContext 61 | , TActivation 62 | , TReactOrder 63 | , TRank 64 | , TUtility 65 | , RNGT 66 | , NSubstitutionLimit 67 | HFSM2_IF_PLANS(, NTaskCapacity) 68 | , TPayload 69 | > 70 | , TApex 71 | > 72 | , public RNGT 73 | { 74 | using Base = RC_< 75 | G_< 76 | NFeatureTag 77 | , TContext 78 | , TActivation 79 | , TReactOrder 80 | , TRank 81 | , TUtility 82 | , RNGT 83 | , NSubstitutionLimit 84 | HFSM2_IF_PLANS(, NTaskCapacity) 85 | , TPayload 86 | > 87 | , TApex 88 | >; 89 | 90 | public: 91 | static constexpr FeatureTag FEATURE_TAG = Base::FEATURE_TAG; 92 | 93 | using typename Base::Context; 94 | 95 | #if HFSM2_LOG_INTERFACE_AVAILABLE() 96 | using typename Base::Logger; 97 | #endif 98 | 99 | public: 100 | HFSM2_CONSTEXPR(14) explicit InstanceT(Context& context 101 | HFSM2_IF_LOG_INTERFACE(, Logger* const logger = nullptr)) noexcept 102 | : Base{context 103 | , static_cast&>(*this) 104 | HFSM2_IF_LOG_INTERFACE(, logger)} 105 | , RNGT{0} 106 | {} 107 | }; 108 | 109 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 110 | // TContext == EmptyContext 111 | // TRNG == RNGT 112 | 113 | /// @brief FSM Root 114 | /// @tparam `TConfig` Type configuration 115 | /// @tparam `TApex` Root region type 116 | template < 117 | FeatureTag NFeatureTag 118 | , typename TActivation 119 | , typename TReactOrder 120 | , typename TRank 121 | , typename TUtility 122 | , Short NSubstitutionLimit 123 | HFSM2_IF_PLANS(, Long NTaskCapacity) 124 | , typename TPayload 125 | , typename TApex 126 | > 127 | class HFSM2_EMPTY_BASES InstanceT< 128 | G_< 129 | NFeatureTag 130 | , EmptyContext 131 | , TActivation 132 | , TReactOrder 133 | , TRank 134 | , TUtility 135 | , RNGT 136 | , NSubstitutionLimit 137 | HFSM2_IF_PLANS(, NTaskCapacity) 138 | , TPayload 139 | > 140 | , TApex 141 | > final 142 | : public RC_< 143 | G_< 144 | NFeatureTag 145 | , EmptyContext 146 | , TActivation 147 | , TReactOrder 148 | , TRank 149 | , TUtility 150 | , RNGT 151 | , NSubstitutionLimit 152 | HFSM2_IF_PLANS(, NTaskCapacity) 153 | , TPayload 154 | > 155 | , TApex 156 | > 157 | , public RNGT 158 | { 159 | using Base = RC_< 160 | G_< 161 | NFeatureTag 162 | , EmptyContext 163 | , TActivation 164 | , TReactOrder 165 | , TRank 166 | , TUtility 167 | , RNGT 168 | , NSubstitutionLimit 169 | HFSM2_IF_PLANS(, NTaskCapacity) 170 | , TPayload 171 | > 172 | , TApex 173 | >; 174 | 175 | public: 176 | static constexpr FeatureTag FEATURE_TAG = Base::FEATURE_TAG; 177 | 178 | #if HFSM2_LOG_INTERFACE_AVAILABLE() 179 | using typename Base::Logger; 180 | #endif 181 | 182 | public: 183 | HFSM2_CONSTEXPR(14) explicit InstanceT(HFSM2_IF_LOG_INTERFACE(Logger* const logger = nullptr)) noexcept 184 | : Base{static_cast&>(*this) 185 | HFSM2_IF_LOG_INTERFACE(, logger)} 186 | , RNGT{0} 187 | {} 188 | }; 189 | 190 | #endif 191 | 192 | //////////////////////////////////////////////////////////////////////////////// 193 | 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /test/test_orthogonal_root.cpp: -------------------------------------------------------------------------------- 1 | // HFSM2 (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | 4 | #define HFSM2_ENABLE_VERBOSE_DEBUG_LOG 5 | #include "tools.hpp" 6 | 7 | using namespace test_tools; 8 | 9 | namespace test_orthogonal_root { 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | using M = hfsm2::Machine; 14 | 15 | using Action = bool; 16 | 17 | //------------------------------------------------------------------------------ 18 | 19 | #define S(s) struct s 20 | 21 | using FSM = M::OrthogonalPeerRoot< 22 | M::Composite 28 | >, 29 | M::Orthogonal, 34 | M::Composite 38 | > 39 | >; 40 | 41 | #undef S 42 | 43 | //------------------------------------------------------------------------------ 44 | 45 | static_assert(FSM::regionId() == 1, ""); 46 | static_assert(FSM::regionId() == 3, ""); 47 | static_assert(FSM::regionId() == 4, ""); 48 | static_assert(FSM::regionId() == 5, ""); 49 | 50 | static_assert(FSM::stateId() == 1, ""); 51 | static_assert(FSM::stateId() == 2, ""); 52 | static_assert(FSM::stateId() == 4, ""); 53 | static_assert(FSM::stateId() == 5, ""); 54 | static_assert(FSM::stateId() == 6, ""); 55 | static_assert(FSM::stateId() == 7, ""); 56 | static_assert(FSM::stateId() == 8, ""); 57 | static_assert(FSM::stateId() == 9, ""); 58 | static_assert(FSM::stateId() == 10, ""); 59 | static_assert(FSM::stateId() == 11, ""); 60 | static_assert(FSM::stateId() == 12, ""); 61 | 62 | //////////////////////////////////////////////////////////////////////////////// 63 | 64 | struct C1 : FSM::State {}; 65 | struct C1_S1 : FSM::State {}; 66 | struct C1_O2_S1 : FSM::State {}; 67 | struct C1_O2_S2 : FSM::State {}; 68 | struct O2 : FSM::State {}; 69 | struct O2_C1 : FSM::State {}; 70 | struct O2_C1_S1 : FSM::State {}; 71 | struct O2_C1_S2 : FSM::State {}; 72 | struct O2_C2 : FSM::State {}; 73 | struct O2_C2_S1 : FSM::State {}; 74 | struct O2_C2_S2 : FSM::State {}; 75 | 76 | //////////////////////////////////////////////////////////////////////////////// 77 | 78 | static_assert(FSM::Instance::Info::STATE_COUNT == 13, ""); 79 | static_assert(FSM::Instance::Info::REGION_COUNT == 6, ""); 80 | static_assert(FSM::Instance::Info::COMPO_COUNT == 3, ""); 81 | static_assert(FSM::Instance::Info::COMPO_PRONGS == 6, ""); 82 | static_assert(FSM::Instance::Info::ORTHO_COUNT == 3, ""); 83 | static_assert(FSM::Instance::Info::ORTHO_UNITS == 3, ""); 84 | 85 | //////////////////////////////////////////////////////////////////////////////// 86 | 87 | TEST_CASE("FSM.Orthogonal Root") { 88 | Logger logger; 89 | 90 | { 91 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 92 | 93 | FSM::Instance machine{&logger}; 94 | { 95 | logger.assertSequence({ 96 | { hfsm2::ROOT_ID , Event::Type::ENTRY_GUARD }, 97 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 98 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 99 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 100 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 101 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 102 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 103 | { FSM::stateId(), Event::Type::ENTRY_GUARD }, 104 | 105 | { hfsm2::ROOT_ID , Event::Type::ENTER }, 106 | { FSM::stateId(), Event::Type::ENTER }, 107 | { FSM::stateId(), Event::Type::ENTER }, 108 | { FSM::stateId(), Event::Type::ENTER }, 109 | { FSM::stateId(), Event::Type::ENTER }, 110 | { FSM::stateId(), Event::Type::ENTER }, 111 | { FSM::stateId(), Event::Type::ENTER }, 112 | { FSM::stateId(), Event::Type::ENTER }, 113 | }); 114 | 115 | assertActive(machine, { 116 | hfsm2::ROOT_ID , 117 | FSM::stateId(), 118 | FSM::stateId(), 119 | FSM::stateId(), 120 | FSM::stateId(), 121 | FSM::stateId(), 122 | FSM::stateId(), 123 | FSM::stateId(), 124 | }); 125 | 126 | assertResumable(machine, {}); 127 | } 128 | 129 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 130 | } 131 | 132 | logger.assertSequence({ 133 | { FSM::stateId(), Event::Type::EXIT }, 134 | { FSM::stateId(), Event::Type::EXIT }, 135 | { FSM::stateId(), Event::Type::EXIT }, 136 | { FSM::stateId(), Event::Type::EXIT }, 137 | { FSM::stateId(), Event::Type::EXIT }, 138 | { FSM::stateId(), Event::Type::EXIT }, 139 | { FSM::stateId(), Event::Type::EXIT }, 140 | { hfsm2::ROOT_ID , Event::Type::EXIT }, 141 | }); 142 | } 143 | 144 | //////////////////////////////////////////////////////////////////////////////// 145 | 146 | } 147 | --------------------------------------------------------------------------------