├── .editorconfig ├── .gitignore ├── appveyor.yml ├── examples ├── basic_streetlight │ ├── CMakeLists.txt │ └── main.cpp ├── advanced_event_handling │ ├── CMakeLists.txt │ └── main.cpp └── debug_logger_interface │ ├── CMakeLists.txt │ └── main.cpp ├── projects └── visual-studio │ ├── basic_streetlight.vcxproj.filters │ ├── basic_event.vcxproj.filters │ ├── basic_logger_interface.filters │ ├── shared.props │ ├── HFSM-tools.pyproj │ ├── HFSM.vcxproj.filters │ ├── HFSM.sln │ ├── basic_streetlight.vcxproj │ ├── basic_event.vcxproj │ ├── basic_logger_interface.vcxproj │ └── HFSM.vcxproj ├── test ├── CMakeLists.txt └── main.cpp ├── include └── hfsm │ └── detail │ ├── type_info.hpp │ ├── array.inl │ ├── iterator.inl │ ├── wrap.hpp │ ├── array_view.hpp │ ├── array_view.inl │ ├── iterator.hpp │ ├── array.hpp │ ├── hash_table.hpp │ ├── machine_state.hpp │ ├── utility.hpp │ ├── hash_table.inl │ ├── machine_state_methods.inl │ ├── machine_orthogonal_sub_2.inl │ ├── machine_orthogonal.hpp │ ├── machine_composite_sub_2.inl │ ├── machine_composite.hpp │ ├── machine_orthogonal_methods.inl │ ├── machine_composite_sub_1.inl │ ├── machine_orthogonal_sub_1.inl │ ├── machine_composite_methods.inl │ └── machine.inl ├── LICENSE ├── tools └── join.py ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── README.md ├── .travis.yml └── hfsm.natvis /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | indent_style = tab 3 | indent_size = 4 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /binaries-* 2 | /build 3 | /hfsm_test.dir/Debug 4 | .idea 5 | .vs 6 | cmake-* 7 | *.user 8 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: "{build}" 2 | 3 | image: 4 | - Visual Studio 2015 5 | - Visual Studio 2017 6 | 7 | configuration: 8 | - Debug 9 | - Release 10 | 11 | platform: 12 | - 32 13 | - 64 14 | 15 | build: 16 | parallel: true 17 | verbosity: normal 18 | -------------------------------------------------------------------------------- /examples/basic_streetlight/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | set(CMAKE_CXX_STANDARD 14) 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas -Werror") 5 | 6 | project(basic_streetlight) 7 | include_directories("${CMAKE_CURRENT_LIST_DIR}/../../include") 8 | add_executable(${PROJECT_NAME} main.cpp) 9 | -------------------------------------------------------------------------------- /examples/advanced_event_handling/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | set(CMAKE_CXX_STANDARD 14) 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas -Werror") 5 | 6 | project(advanced_event_handling) 7 | include_directories("${CMAKE_CURRENT_LIST_DIR}/../../include") 8 | add_executable(${PROJECT_NAME} main.cpp) 9 | -------------------------------------------------------------------------------- /examples/debug_logger_interface/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | set(CMAKE_CXX_STANDARD 14) 4 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas -Werror") 5 | 6 | project(debug_logger_interface) 7 | include_directories("${CMAKE_CURRENT_LIST_DIR}/../../include") 8 | add_executable(${PROJECT_NAME} main.cpp) 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_streetlight.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_event.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #------------------------------------------------------------------------------- 2 | # hfsm_test target 3 | #------------------------------------------------------------------------------- 4 | add_executable(hfsm_test main.cpp) 5 | target_link_libraries(hfsm_test hfsm) 6 | add_dependencies(hfsm_test hfsm) 7 | 8 | #------------------------------------------------------------------------------- 9 | # Add tests 10 | #------------------------------------------------------------------------------- 11 | add_test(NAME hfsm_test COMMAND hfsm_test) 12 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_logger_interface.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {f36e39b3-d6a6-4d9f-be7c-7081167a0b62} 6 | 7 | 8 | 9 | 10 | _ 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /include/hfsm/detail/type_info.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "wrap.hpp" 4 | 5 | namespace hfsm { 6 | namespace detail { 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | class TypeInfo 11 | : public Wrap 12 | { 13 | using Base = Wrap; 14 | 15 | public: 16 | typedef std::type_index Native; 17 | 18 | public: 19 | inline TypeInfo() = default; 20 | 21 | inline TypeInfo(const std::type_index type) 22 | : Base(type) 23 | {} 24 | 25 | template 26 | static inline TypeInfo get() { return TypeInfo(typeid(T)); } 27 | }; 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /include/hfsm/detail/array.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | T& 8 | StaticArray::operator[] (const unsigned i) { 9 | assert(0 <= i && i < CAPACITY); 10 | 11 | return _items[i]; 12 | } 13 | 14 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 15 | 16 | template 17 | const T& 18 | StaticArray::operator[] (const unsigned i) const { 19 | assert(0 <= i && i < CAPACITY); 20 | 21 | return _items[i]; 22 | } 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 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 | -------------------------------------------------------------------------------- /projects/visual-studio/shared.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | $(SolutionDir)..\..\binaries-$(PlatformArchitecture)\ 7 | $(BUILD_ROOT)\$(SolutionName)-$(PlatformArchitecture)\$(ProjectName)-$(Configuration)\ 8 | $(ProjectName)-$(Configuration)-$(PlatformArchitecture) 9 | 10 | 11 | 12 | $(SolutionDir)../../include;%(AdditionalIncludeDirectories) 13 | Level4 14 | true 15 | _UNICODE;UNICODE;_CONSOLE;%(PreprocessorDefinitions) 16 | /std:c++latest 17 | 18 | 19 | Console 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /include/hfsm/detail/iterator.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | bool 8 | Iterator::operator != (const Iterator& HSFM_IF_ASSERT(dummy)) const { 9 | assert(&_container == &dummy._container); 10 | 11 | return _cursor != _container.limit(); 12 | } 13 | 14 | //------------------------------------------------------------------------------ 15 | 16 | template 17 | Iterator& 18 | Iterator::operator ++() { 19 | _cursor = _container.next(_cursor); 20 | 21 | return *this; 22 | } 23 | 24 | //////////////////////////////////////////////////////////////////////////////// 25 | 26 | template 27 | bool 28 | Iterator::operator != (const Iterator& HSFM_IF_ASSERT(dummy)) const { 29 | assert(&_container == &dummy._container); 30 | 31 | return _cursor != _container.limit(); 32 | } 33 | 34 | //------------------------------------------------------------------------------ 35 | 36 | template 37 | Iterator& 38 | Iterator::operator ++() { 39 | _cursor = _container.next(_cursor); 40 | 41 | return *this; 42 | } 43 | 44 | //////////////////////////////////////////////////////////////////////////////// 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tools/join.py: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | 3 | def mergeTo(folder, path, included, pragmaOnceCounter, output): 4 | pathTokens = path.split("/") 5 | 6 | if len(pathTokens) > 1: 7 | folder += "/" + pathTokens[0] 8 | 9 | current = folder + "/" + pathTokens[-1] 10 | with open(current, 'r', encoding='utf-8') as input: 11 | for line in input: 12 | 13 | if line.startswith('#include "'): 14 | next = line[10 : -2] 15 | 16 | if next not in included: 17 | nextTokens = next.split("/") 18 | included.append(nextTokens[-1]) 19 | 20 | #output.write("// inlined '" + pathTokens[-1] + "' -> '" + nextTokens[-1] + "'\n") 21 | 22 | if len(nextTokens) == 1: 23 | mergeTo(folder, next, included, pragmaOnceCounter, output) 24 | else: 25 | mergeTo(folder + "/" + nextTokens[0], nextTokens[-1], included, pragmaOnceCounter, output) 26 | else: 27 | if line.startswith('\ufeff'): 28 | line = line[1:] 29 | 30 | if line.startswith('#pragma'): 31 | pragma = line[8:] 32 | 33 | if pragma.startswith('once'): 34 | pragmaOnceCounter += 1 35 | if pragmaOnceCounter > 1: 36 | continue 37 | 38 | elif pragma.startswith('region') or pragma.startswith('endregion'): 39 | continue 40 | 41 | output.write(line) 42 | 43 | ################################################################################ 44 | 45 | output = open("../include/hfsm/machine_single.hpp", 'w', encoding='utf-8-sig') 46 | included = [] 47 | pragmaOnceCounter = 0 48 | mergeTo("../include", "hfsm/machine.hpp", included, pragmaOnceCounter, output) 49 | 50 | output.close() 51 | 52 | ################################################################################ 53 | -------------------------------------------------------------------------------- /include/hfsm/detail/wrap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace hfsm { 4 | namespace detail { 5 | 6 | //------------------------------------------------------------------------------ 7 | 8 | template 9 | class Wrap; 10 | 11 | //////////////////////////////////////////////////////////////////////////////// 12 | 13 | template 14 | class Wrap { 15 | using Item = T; 16 | using Storage = typename std::aligned_storage::type; 17 | 18 | public: 19 | inline Wrap() = default; 20 | 21 | template 22 | inline void create(Ts&&... ts) { new (&get()) Item(std::forward(ts)...); } 23 | 24 | inline Wrap(const Item& item) { get() = item; } 25 | inline Wrap(Item&& item) { get() = std::move(item); } 26 | 27 | inline Wrap& operator = (const Item& item) { get() = item; return *this; } 28 | inline Wrap& operator = (Item&& item) { get() = std::move(item); return *this; } 29 | 30 | inline void clear() { fill(_storage, 0); } 31 | 32 | inline T& operator *() { return get(); } 33 | inline const T& operator *() const { return get(); } 34 | 35 | inline T* operator->() { return &get(); } 36 | inline const T* operator->() const { return &get(); } 37 | 38 | inline explicit operator bool() const { return _storage != 0; } 39 | 40 | inline bool operator == (const Wrap other) const { return get() == other.get(); } 41 | 42 | private: 43 | inline T& get() { return *reinterpret_cast< T* const>(&_storage); } 44 | inline const T& get() const { return *reinterpret_cast(&_storage); } 45 | 46 | private: 47 | Storage _storage; 48 | }; 49 | 50 | //////////////////////////////////////////////////////////////////////////////// 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /include/hfsm/detail/array_view.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace hfsm { 4 | namespace detail { 5 | 6 | //////////////////////////////////////////////////////////////////////////////// 7 | 8 | #pragma pack(push, 4) 9 | 10 | template 11 | class ArrayView { 12 | public: 13 | using Item = T; 14 | 15 | template 16 | friend class Iterator; 17 | 18 | protected: 19 | ArrayView(const unsigned capacity); 20 | ~ArrayView(); 21 | 22 | public: 23 | inline void clear() { _count = 0; } 24 | 25 | inline unsigned resize(const unsigned count); 26 | 27 | template 28 | inline unsigned operator << (TValue&& value); 29 | 30 | inline Item& operator[] (const unsigned i) { return get(i); } 31 | inline const Item& operator[] (const unsigned i) const { return get(i); } 32 | 33 | inline unsigned capacity() const { return _capacity; } 34 | inline unsigned count() const { return _count; } 35 | 36 | protected: 37 | inline unsigned first() const { return 0; } 38 | inline unsigned limit() const { return _count; } 39 | 40 | inline unsigned prev(const unsigned i) const { return i - 1; } 41 | inline unsigned next(const unsigned i) const { return i + 1; } 42 | 43 | inline Item& get(const unsigned i); 44 | inline const Item& get(const unsigned i) const; 45 | 46 | private: 47 | // hacks 48 | inline Item* data() { return reinterpret_cast< Item*>(&_count + 1); } 49 | inline const Item* data() const { return reinterpret_cast(&_count + 1); } 50 | 51 | protected: 52 | const unsigned _capacity; 53 | unsigned _count = 0; 54 | }; 55 | 56 | #pragma pack(pop) 57 | 58 | //////////////////////////////////////////////////////////////////////////////// 59 | 60 | } 61 | } 62 | 63 | #include "array_view.inl" 64 | -------------------------------------------------------------------------------- /projects/visual-studio/HFSM-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.6 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 | -------------------------------------------------------------------------------- /include/hfsm/detail/array_view.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | ArrayView::ArrayView(const unsigned capacity) 8 | : _capacity(capacity) 9 | {} 10 | 11 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 12 | 13 | template 14 | ArrayView::~ArrayView() { 15 | if (_count > 0) 16 | for (int i = _count - 1; i >= 0; --i) 17 | get(i).~Item(); 18 | } 19 | 20 | //------------------------------------------------------------------------------ 21 | 22 | template 23 | unsigned 24 | ArrayView::resize(const unsigned count) { 25 | const unsigned clampedCount = count < _capacity ? 26 | count : _capacity; 27 | 28 | if (clampedCount > _count) { 29 | for (unsigned i = _count; i < clampedCount; ++i) 30 | get(i) = Item(); 31 | } 32 | else if (clampedCount < _count) { 33 | for (unsigned i = _count - 1; i >= clampedCount; --i) 34 | get(i).~Item(); 35 | } 36 | 37 | return _count = clampedCount; 38 | } 39 | 40 | //------------------------------------------------------------------------------ 41 | 42 | template 43 | template 44 | unsigned 45 | ArrayView::operator << (TValue&& value) { 46 | assert(_count < _capacity); 47 | 48 | new (&get(_count)) Item(std::move(value)); 49 | 50 | return _count++; 51 | } 52 | 53 | //------------------------------------------------------------------------------ 54 | 55 | template 56 | T& 57 | ArrayView::get(const unsigned i) { 58 | assert(0 <= i && i < _capacity); 59 | 60 | return data()[i]; 61 | } 62 | 63 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 64 | 65 | template 66 | const T& 67 | ArrayView::get(const unsigned i) const { 68 | assert(0 <= i && i < _capacity); 69 | 70 | return data()[i]; 71 | } 72 | 73 | //////////////////////////////////////////////////////////////////////////////// 74 | 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /include/hfsm/detail/iterator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utility.hpp" 4 | 5 | namespace hfsm { 6 | namespace detail { 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | template 11 | class Iterator { 12 | public: 13 | using Container = TContainer; 14 | using Item = typename Container::Item; 15 | 16 | template 17 | friend class Array; 18 | 19 | private: 20 | inline Iterator(Container& container, const unsigned cursor) 21 | : _container(container) 22 | , _cursor(cursor) 23 | {} 24 | 25 | public: 26 | inline bool operator != (const Iterator& dummy) const; 27 | 28 | inline Iterator& operator ++(); 29 | 30 | inline Item& operator *() { return _container[_cursor]; } 31 | inline const Item& operator *() const { return _container[_cursor]; } 32 | 33 | inline Item* operator->() { return &operator *(); } 34 | inline const Item* operator->() const { return &operator *(); } 35 | 36 | private: 37 | Container& _container; 38 | 39 | unsigned _cursor; 40 | }; 41 | 42 | //------------------------------------------------------------------------------ 43 | 44 | template 45 | class Iterator { 46 | public: 47 | using Container = TContainer; 48 | using Item = typename Container::Item; 49 | 50 | template 51 | friend class Array; 52 | 53 | private: 54 | inline Iterator(const Container& container, const unsigned cursor) 55 | : _container(container) 56 | , _cursor(cursor) 57 | {} 58 | 59 | public: 60 | inline bool operator != (const Iterator& dummy) const; 61 | 62 | inline Iterator& operator ++(); 63 | 64 | inline const Item& operator *() const { return _container[_cursor]; } 65 | 66 | inline const Item* operator->() const { return &operator *(); } 67 | 68 | private: 69 | const Container& _container; 70 | 71 | unsigned _cursor; 72 | }; 73 | 74 | //////////////////////////////////////////////////////////////////////////////// 75 | 76 | } 77 | } 78 | 79 | #include "iterator.inl" 80 | -------------------------------------------------------------------------------- /include/hfsm/detail/array.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "array_view.hpp" 4 | #include "iterator.hpp" 5 | 6 | namespace hfsm { 7 | namespace detail { 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | 11 | #pragma pack(push, 4) 12 | 13 | template 14 | class StaticArray { 15 | public: 16 | enum { 17 | CAPACITY = TCapacity, 18 | }; 19 | 20 | using Item = T; 21 | using Index = unsigned char; // TODO: adjust to CAPACITY 22 | static_assert(CAPACITY <= std::numeric_limits::max(), ""); 23 | 24 | public: 25 | inline StaticArray() = default; 26 | 27 | inline Item& operator[] (const unsigned i); 28 | inline const Item& operator[] (const unsigned i) const; 29 | 30 | inline const unsigned count() const { return CAPACITY; } 31 | 32 | private: 33 | Item _items[CAPACITY]; 34 | }; 35 | 36 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 37 | 38 | template 39 | struct StaticArray {}; 40 | 41 | //------------------------------------------------------------------------------ 42 | 43 | template 44 | class Array 45 | : public ArrayView 46 | { 47 | public: 48 | enum : unsigned { 49 | CAPACITY = TCapacity, 50 | INVALID = (unsigned)-1, 51 | DUMMY = INVALID, 52 | }; 53 | 54 | using View = ArrayView; 55 | using Item = typename View::Item; 56 | 57 | public: 58 | Array() 59 | : View(CAPACITY) 60 | { 61 | assert(&View::get(0) == _storage); 62 | } 63 | 64 | inline Iterator< Array> begin() { return Iterator< Array>(*this, View::first()); } 65 | inline Iterator begin() const { return Iterator(*this, View::first()); } 66 | inline Iterator cbegin() const { return Iterator(*this, View::first()); } 67 | 68 | inline Iterator< Array> end() { return Iterator< Array>(*this, DUMMY); } 69 | inline Iterator end() const { return Iterator(*this, DUMMY); } 70 | inline Iterator cend() const { return Iterator(*this, DUMMY); } 71 | 72 | private: 73 | Item _storage[CAPACITY]; 74 | }; 75 | 76 | #pragma pack(pop) 77 | 78 | //////////////////////////////////////////////////////////////////////////////// 79 | 80 | } 81 | } 82 | 83 | #include "array.inl" 84 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1 FATAL_ERROR) 2 | project(hfsm VERSION 0.1.0 LANGUAGES CXX) 3 | 4 | include(GNUInstallDirs) 5 | 6 | option(HFSM_BUILD_TESTS "Enable/disable building test executable" ON) 7 | 8 | set(CMAKE_CXX_STANDARD 14) 9 | set(CMAKE_CXX_EXTENSIONS OFF) 10 | 11 | set(extra_cxx_flags "-Wall -Wno-unknown-pragmas -Werror") 12 | set(CMAKE_CXX_FLAGS "${extra_cxx_flags} ${CMAKE_CXX_FLAGS}") 13 | set(CMAKE_C_FLAGS "${extra_cxx_flags} ${CMAKE_C_FLAGS}") 14 | 15 | #------------------------------------------------------------------------------- 16 | # hfsm target 17 | #------------------------------------------------------------------------------- 18 | add_library(hfsm INTERFACE) 19 | target_include_directories( 20 | hfsm INTERFACE 21 | "$" 22 | "$" 23 | ) 24 | 25 | #------------------------------------------------------------------------------- 26 | # Install & export targets 27 | #------------------------------------------------------------------------------- 28 | set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") 29 | set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}") 30 | set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake") 31 | set(project_config "${PROJECT_NAME}Config.cmake") 32 | set(targets_export_name "${PROJECT_NAME}Targets") 33 | set(namespace "${PROJECT_NAME}::") 34 | 35 | # Generate package version file. 36 | include(CMakePackageConfigHelpers) 37 | write_basic_package_version_file( 38 | "${version_config}" COMPATIBILITY SameMajorVersion 39 | ) 40 | 41 | # Export hfsm target. 42 | install( 43 | TARGETS hfsm 44 | EXPORT "${targets_export_name}" 45 | INCLUDES DESTINATION "${include_install_dir}" 46 | ) 47 | 48 | # Install hfsm headers. 49 | install( 50 | DIRECTORY "include/${PROJECT_NAME}" 51 | DESTINATION "${include_install_dir}" 52 | ) 53 | 54 | # Install project version file. 55 | install( 56 | FILES "${version_config}" 57 | DESTINATION "${config_install_dir}" 58 | ) 59 | 60 | # Install project config file. 61 | install( 62 | EXPORT "${targets_export_name}" 63 | NAMESPACE "${namespace}" 64 | DESTINATION "${config_install_dir}" 65 | FILE ${project_config} 66 | ) 67 | 68 | # Export build directory config file. 69 | export( 70 | EXPORT ${targets_export_name} 71 | NAMESPACE "${namespace}" 72 | FILE ${project_config} 73 | ) 74 | 75 | # Register project in CMake user registry. 76 | export(PACKAGE ${PROJECT_NAME}) 77 | 78 | if(HFSM_BUILD_TESTS) 79 | enable_testing() 80 | add_subdirectory(test) 81 | endif(HFSM_BUILD_TESTS) 82 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at andrew.gresyk@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /include/hfsm/detail/hash_table.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "utility.hpp" 4 | #include "wrap.hpp" 5 | 6 | namespace hfsm { 7 | namespace detail { 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | // Open-addressing, Robin Hood hashing associative container 11 | 12 | template > 16 | class HashTable { 17 | public: 18 | enum : unsigned { 19 | REQUESTED_CAPACITY = TCapacity, 20 | CAPACITY = NextPowerOf2::Value, 21 | INDEX_MASK = CAPACITY - 1, 22 | USED_SHIFT = 31u, 23 | USED_MASK = 1u << USED_SHIFT, 24 | HASH_MASK = ~USED_MASK, 25 | INVALID = (unsigned)-1, 26 | DUMMY = INVALID, 27 | }; 28 | static_assert(CAPACITY <= HASH_MASK, "Capacity needs to be less than HASH_MASK 0x7fffffff"); 29 | 30 | using Hash = unsigned; 31 | using Key = TKey; 32 | using Value = TValue; 33 | using Hasher = THasher; 34 | 35 | //---------------------------------------------------------------------- 36 | 37 | class Item { 38 | public: 39 | inline Item() = default; 40 | 41 | inline Item(const Hash hash, const Key key); 42 | inline Item(const Hash hash, const Key key, const Value value); 43 | 44 | inline void swap(Item& other); 45 | inline bool operator == (const Item& other) const; 46 | 47 | inline unsigned hash() const { return _hash & HASH_MASK; } 48 | 49 | inline bool vacant() const { return _hash >> USED_SHIFT == 0; } 50 | inline bool occupied() const { return _hash >> USED_SHIFT != 0; } 51 | inline void vacate() { _hash &= !USED_MASK; } 52 | 53 | inline const Key& key() const { return *_keyWrap; } 54 | 55 | inline Value* value() { return &_value; } 56 | inline const Value* value() const { return &_value; } 57 | 58 | private: 59 | Hash _hash = 0; 60 | Wrap _keyWrap; 61 | Value _value; 62 | }; 63 | typedef Item Items[CAPACITY]; 64 | 65 | //---------------------------------------------------------------------- 66 | 67 | struct Stats { 68 | inline Stats(const float loadFactor_, const float averageProbeCount_) 69 | : loadFactor(loadFactor_) 70 | , averageProbeCount(averageProbeCount_) 71 | {} 72 | 73 | float loadFactor = 0.0f; 74 | float averageProbeCount = 0.0f; 75 | }; 76 | 77 | //---------------------------------------------------------------------- 78 | 79 | public: 80 | HashTable() = default; 81 | 82 | bool insert(const Key key, const Value value); 83 | 84 | Value* find(const Key key); 85 | const Value* find(const Key key) const; 86 | 87 | inline unsigned count() const { return _count; } 88 | 89 | private: 90 | unsigned locate(const Key key) const; 91 | 92 | inline unsigned probeCount(const unsigned i) const; 93 | 94 | inline unsigned index(const unsigned hash) const { return hash & INDEX_MASK; } 95 | inline unsigned hash(const Key key) const { return Hasher()(key) & HASH_MASK; } 96 | 97 | inline unsigned skipVacantForward(const unsigned i) const; 98 | 99 | private: 100 | Items _items; 101 | unsigned _count = 0; 102 | }; 103 | 104 | //////////////////////////////////////////////////////////////////////////////// 105 | 106 | } 107 | } 108 | 109 | #include "hash_table.inl" 110 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_state.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | struct M::_S { 8 | using Head = TH; 9 | 10 | enum : unsigned { 11 | ReverseDepth = 1, 12 | DeepWidth = 0, 13 | StateCount = 1, 14 | ForkCount = 0, 15 | ProngCount = 0, 16 | Width = 1, 17 | }; 18 | 19 | _S(StateRegistry& stateRegistry, 20 | const Parent parent, 21 | Parents& stateParents, 22 | Parents& forkParents, 23 | ForkPointers& forkPointers); 24 | 25 | inline void deepForwardSubstitute (Control&, Context&, LoggerInterface* const) {} 26 | inline bool deepSubstitute (Control& control, Context& context, LoggerInterface* const logger); 27 | 28 | inline void deepEnterInitial ( Context& context, LoggerInterface* const logger); 29 | inline void deepEnter ( Context& context, LoggerInterface* const logger); 30 | 31 | inline bool deepUpdateAndTransition (Control& control, Context& context, LoggerInterface* const logger); 32 | inline void deepUpdate ( Context& context, LoggerInterface* const logger); 33 | 34 | template 35 | inline void deepReact (const TEvent& event, 36 | Control& control, Context& context, LoggerInterface* const logger); 37 | 38 | inline void deepLeave ( Context& context, LoggerInterface* const logger); 39 | 40 | inline void deepForwardRequest(const enum Transition::Type) {} 41 | inline void deepRequestRemain() {} 42 | inline void deepRequestRestart() {} 43 | inline void deepRequestResume() {} 44 | inline void deepChangeToRequested ( Context&, LoggerInterface* const) {} 45 | 46 | #if defined HFSM_ENABLE_STRUCTURE_REPORT || defined HFSM_ENABLE_LOG_INTERFACE 47 | static constexpr bool isBare() { return std::is_same::value; } 48 | 49 | enum : unsigned { 50 | NameCount = isBare() ? 0 : 1, 51 | }; 52 | #endif 53 | 54 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 55 | static const char* name(); 56 | 57 | void deepGetNames(const unsigned parent, 58 | const enum StateInfo::RegionType region, 59 | const unsigned depth, 60 | StateInfos& stateInfos) const; 61 | 62 | void deepIsActive(const bool isActive, 63 | unsigned& index, 64 | MachineStructure& structure) const; 65 | #endif 66 | 67 | #ifdef HFSM_ENABLE_LOG_INTERFACE 68 | static const char* fullName(); 69 | 70 | template 71 | struct MemberTraits; 72 | 73 | template 74 | struct MemberTraits { 75 | using State = TState; 76 | }; 77 | 78 | template 79 | typename std::enable_if< std::is_same::State, Base>::value>::type 80 | log(LoggerInterface&) const {} 81 | 82 | template 83 | typename std::enable_if::State, Base>::value>::type 84 | log(LoggerInterface& logger) const { 85 | logger.record(typeid(Head), fullName(), TMethodId, methodName(TMethodId)); 86 | } 87 | #endif 88 | 89 | Head _head; 90 | 91 | HSFM_IF_DEBUG(const TypeInfo _type = TypeInfo::get()); 92 | }; 93 | 94 | //////////////////////////////////////////////////////////////////////////////// 95 | 96 | } 97 | 98 | #include "machine_state_methods.inl" 99 | -------------------------------------------------------------------------------- /projects/visual-studio/HFSM.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {25afde4c-8636-489f-9a74-e87f38b09cb9} 6 | 7 | 8 | {25fa5477-2735-467a-a226-594457ffc81b} 9 | 10 | 11 | {bcadbaf6-c69d-493f-b835-a441b888682d} 12 | 13 | 14 | 15 | 16 | test 17 | 18 | 19 | 20 | 21 | hfsm 22 | 23 | 24 | hfsm\detail 25 | 26 | 27 | hfsm\detail 28 | 29 | 30 | hfsm\detail 31 | 32 | 33 | hfsm\detail 34 | 35 | 36 | hfsm\detail 37 | 38 | 39 | hfsm\detail 40 | 41 | 42 | hfsm\detail 43 | 44 | 45 | hfsm 46 | 47 | 48 | 49 | 50 | hfsm\detail 51 | 52 | 53 | hfsm\detail 54 | 55 | 56 | hfsm\detail 57 | 58 | 59 | hfsm\detail 60 | 61 | 62 | hfsm\detail 63 | 64 | 65 | hfsm\detail 66 | 67 | 68 | hfsm\detail 69 | 70 | 71 | hfsm\detail 72 | 73 | 74 | hfsm\detail 75 | 76 | 77 | hfsm\detail 78 | 79 | 80 | hfsm\detail 81 | 82 | 83 | hfsm\detail 84 | 85 | 86 | hfsm\detail 87 | 88 | 89 | hfsm\detail 90 | 91 | 92 | hfsm\detail 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /include/hfsm/detail/utility.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined _DEBUG && _MSC_VER 4 | #include // __debugbreak() 5 | #endif 6 | 7 | //------------------------------------------------------------------------------ 8 | 9 | #if defined _DEBUG && _MSC_VER 10 | #define HSFM_BREAK() __debugbreak() 11 | #define HSFM_CHECKED(x) (!!(x) || (HSFM_BREAK(), 0)) 12 | #else 13 | #define HSFM_BREAK() ((void) 0) 14 | #define HSFM_CHECKED(x) x 15 | #endif 16 | 17 | #ifdef _DEBUG 18 | #define HSFM_IF_DEBUG(...) __VA_ARGS__ 19 | #else 20 | #define HSFM_IF_DEBUG(...) 21 | #endif 22 | 23 | #ifndef NDEBUG 24 | #define HSFM_IF_ASSERT(...) __VA_ARGS__ 25 | #else 26 | #define HSFM_IF_ASSERT(...) 27 | #endif 28 | 29 | //------------------------------------------------------------------------------ 30 | 31 | namespace hfsm { 32 | namespace detail { 33 | 34 | //////////////////////////////////////////////////////////////////////////////// 35 | 36 | template 37 | inline 38 | void 39 | fill(T& a, const char value) { 40 | memset(&a, (int) value, sizeof(a)); 41 | } 42 | 43 | //////////////////////////////////////////////////////////////////////////////// 44 | 45 | template 46 | inline 47 | unsigned 48 | count(const T(&)[TCount]) { 49 | return TCount; 50 | } 51 | 52 | //------------------------------------------------------------------------------ 53 | 54 | template 55 | inline 56 | const T* 57 | end(const T(& a)[TCapacity]) { 58 | return &a[TCapacity]; 59 | } 60 | 61 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 62 | 63 | template 64 | inline 65 | const TReturn* 66 | end(const T(& a)[TCapacity]) { 67 | return reinterpret_cast(&a[TCapacity]); 68 | } 69 | 70 | //////////////////////////////////////////////////////////////////////////////// 71 | 72 | template 73 | struct Min { 74 | enum { 75 | Value = t1 < t2 ? t1 : t2 76 | }; 77 | }; 78 | 79 | //------------------------------------------------------------------------------ 80 | 81 | template 82 | struct Max { 83 | enum { 84 | Value = t1 > t2 ? t1 : t2 85 | }; 86 | }; 87 | 88 | //////////////////////////////////////////////////////////////////////////////// 89 | 90 | template 91 | struct PowerOf2 { 92 | enum { 93 | Value = !(t & (t - 1)) 94 | }; 95 | }; 96 | 97 | //------------------------------------------------------------------------------ 98 | 99 | template 100 | struct BitCount { 101 | enum { 102 | Value = 103 | t == 0 ? 0 : 104 | t >> 1 == 0 ? 1 : 105 | t >> 2 == 0 ? 2 : 106 | t >> 3 == 0 ? 3 : 107 | t >> 4 == 0 ? 4 : 108 | t >> 5 == 0 ? 5 : 109 | t >> 6 == 0 ? 6 : 110 | t >> 7 == 0 ? 7 : 111 | 112 | t >> 8 == 0 ? 8 : 113 | t >> 9 == 0 ? 9 : 114 | t >> 10 == 0 ? 10 : 115 | t >> 11 == 0 ? 11 : 116 | t >> 12 == 0 ? 12 : 117 | t >> 13 == 0 ? 13 : 118 | t >> 14 == 0 ? 14 : 119 | t >> 15 == 0 ? 15 : 120 | 121 | t >> 16 == 0 ? 16 : 122 | t >> 17 == 0 ? 17 : 123 | t >> 18 == 0 ? 18 : 124 | t >> 19 == 0 ? 19 : 125 | t >> 20 == 0 ? 20 : 126 | t >> 21 == 0 ? 21 : 127 | t >> 22 == 0 ? 22 : 128 | t >> 23 == 0 ? 23 : 129 | 130 | t >> 24 == 0 ? 24 : 131 | t >> 25 == 0 ? 25 : 132 | t >> 26 == 0 ? 26 : 133 | t >> 27 == 0 ? 27 : 134 | t >> 28 == 0 ? 28 : 135 | t >> 29 == 0 ? 29 : 136 | t >> 30 == 0 ? 30 : 137 | t >> 31 == 0 ? 31 : 138 | 32 139 | }; 140 | }; 141 | 142 | //------------------------------------------------------------------------------ 143 | 144 | template 145 | struct NextPowerOf2 { 146 | enum { 147 | Value = PowerOf2::Value ? 148 | t : 1 << BitCount::Value 149 | }; 150 | }; 151 | 152 | //////////////////////////////////////////////////////////////////////////////// 153 | 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Standard](https://img.shields.io/badge/c%2B%2B-14/17/20-blue.svg)](https://en.wikipedia.org/wiki/C%2B%2B#Standardization) 2 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 3 | [![Build status](https://ci.appveyor.com/api/projects/status/49gona9jtghvvi6g?svg=true)](https://ci.appveyor.com/project/andrew-gresyk/hfsm) 4 | [![Build Status](https://travis-ci.org/andrew-gresyk/HFSM.svg?branch=master)](https://travis-ci.org/andrew-gresyk/HFSM) 5 | 6 | # HFSM (Hierarchical Finite State Machine) Framework 7 | 8 | Header-only heriarchical FSM framework in C++14, completely static (no dynamic allocations), built with variadic templates. 9 | 10 | ## Compiler Support 11 | 12 | DEPRECATED, FURTHER DEVELOPMENT CONTINUES IN [HFSM2](https://github.com/andrew-gresyk/HFSM2) 13 | 14 | ## Compiler Support 15 | 16 | - Visual Studio 14.u3, 15.7 17 | - GCC 4.9, 5.4, 6.3, 7.3, 8.0 18 | - Clang 3.6, 3.7, 3.8, 3.9, 4.0, 5.0, 6.0 19 | 20 | --- 21 | 22 | ## Basic Usage 23 | 24 | ```cpp 25 | // 1. Include HFSM header: 26 | #include 27 | 28 | // 2. Define interface class between the FSM and its owner 29 | // (also ok to use the owner object itself): 30 | struct Context { /* ... */ }; 31 | 32 | // 3. (Optional) Typedef hfsm::Machine for convenience: 33 | using M = hfsm::Machine; 34 | 35 | // 4. Define states: 36 | struct MyState1 : M::Bare { 37 | // 5. Override some of the following state functions: 38 | void enter(Context& _); 39 | void update(Context& _); 40 | void transition(Control& c, Context& _); 41 | void leave(Context& _); 42 | }; 43 | 44 | struct MyState2 : M::Bare { /* .. */ }; 45 | struct MySubState1 : M::Bare { /* .. */ }; 46 | struct MySubState2 : M::Bare { /* .. */ }; 47 | 48 | struct MyState3 : M::Bare { /* .. */ }; 49 | struct MyOrthogonalState1 : M::Bare { /* .. */ }; 50 | struct MyOrthogonalState2 : M::Bare { /* .. */ }; 51 | 52 | // 6. Declare state machine structure: 53 | using MyFSM = M::PeerRoot< 54 | MyState1, 55 | M::Composite, 59 | M::Orthogonal 63 | >; 64 | 65 | int main() { 66 | // 7. Create context and state machine instances: 67 | Context context; 68 | MyFSM fsm(context); 69 | 70 | // 8. Kick off periodic updates: 71 | bool running = true; 72 | while (running) 73 | fsm.update(); 74 | 75 | return 0; 76 | } 77 | ``` 78 | 79 | --- 80 | 81 | ## Feature Highlights 82 | 83 | - Permissive [MIT License](LICENSE.md) 84 | - Written in widely-supported modern(ish) C++ 14 85 | - 100% NoUML-compliant 86 | - Not hamstrung by restrictive event reaction-based approach, but supports powerful event handling 87 | - Hierarchical, with composite (sub-machine) and orthogonal regions 88 | - Header-only 89 | - Fully static, no dynamic allocations 90 | - Uses inline-friendly compile-time pylymorphism, no virtual methods were harmed 91 | - Type-safe transitions: `FSM.changeTo()` 92 | - Gamedev-friendly, supports explicit `State::update()` 93 | - Scaleable, supports state re-use via state injections 94 | - Debug-assisted, includes automatic structure and activity visualization API with `#define HFSM_ENABLE_STRUCTURE_REPORT` 95 | - Convenient, minimal boilerplate 96 | 97 | --- 98 | 99 | ## Documentation 100 | 101 | See [Wiki](../../wiki) for [Tutorial](../../wiki/Tutorial) and docs. 102 | 103 | --- 104 | 105 | ## Get Updates 106 | 107 | - [Blog](https://andrew-gresyk.github.io/) 108 | - [Twitter](https://www.twitter.com/andrew_gresyk) 109 | 110 | --- 111 | 112 | ## Special Thanks 113 | 114 | - [Phil Nash](https://github.com/philsquared) 115 | - [Romain Cheminade](https://github.com/romaincheminade) 116 | - [Tristan Brindle](https://github.com/tcbrindle) 117 | - [Kevin Greene](https://github.com/kgreenek) 118 | - everybody at [C++::London](https://www.meetup.com/CppLondon/) meetup 119 | - programming community at [Splash Damage](http://www.splashdamage.com/) -------------------------------------------------------------------------------- /examples/basic_streetlight/main.cpp: -------------------------------------------------------------------------------- 1 | // HFSM (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | // 4 | // Streetlight FSM example: 5 | // Cycle between R-Y-G-Y-B three times, then turn off 6 | 7 | // State structure: 8 | // 9 | // Root 10 | // ├ On 11 | // │ ├ Red 12 | // │ ├ YellowDownwards 13 | // │ ├ YellowUpwards 14 | // │ └ Green 15 | // └ Off 16 | 17 | // Output: 18 | // 19 | // On 20 | // Red 21 | // Yellow v 22 | // Green 23 | // Yellow ^ 24 | // Red 25 | // Yellow v 26 | // Green 27 | // Yellow ^ 28 | // Red 29 | // Yellow v 30 | // Green 31 | // Yellow ^ 32 | // Red 33 | // Off 34 | 35 | // optional: enable FSM structure report in debugger 36 | #define HFSM_ENABLE_STRUCTURE_REPORT 37 | 38 | #include 39 | 40 | #include 41 | 42 | //------------------------------------------------------------------------------ 43 | 44 | // data shared between FSM states and outside code 45 | struct Context { 46 | unsigned cycleCount; 47 | }; 48 | 49 | // convenience typedef 50 | using M = hfsm::Machine; 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | 54 | // forward declared for Red::transition() 55 | struct Off; 56 | 57 | // top-level region in the hierarchy 58 | struct On 59 | : M::Base // necessary boilerplate! 60 | { 61 | // called on state entry 62 | void enter(Context& context) { 63 | context.cycleCount = 0; 64 | std::cout << "On" << std::endl; 65 | } 66 | 67 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 68 | 69 | // forward declared for Red::transition() 70 | struct YellowDownwards; 71 | 72 | // sub-states 73 | struct Red 74 | : M::Base 75 | { 76 | void enter(Context& context) { 77 | ++context.cycleCount; 78 | std::cout << " Red" << std::endl; 79 | } 80 | 81 | // state can initiate transitions to _any_ other state 82 | void transition(Control& control, Context& context) { 83 | // multiple transitions can be initiated, can be useful in a hierarchy 84 | if (context.cycleCount > 3) 85 | control.changeTo(); 86 | else 87 | control.changeTo(); 88 | } 89 | }; 90 | 91 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 92 | 93 | // forward declared for transition() 94 | struct Green; 95 | 96 | struct YellowDownwards 97 | : M::Base 98 | { 99 | void enter(Context&) { 100 | std::cout << " Yellow v" << std::endl; 101 | } 102 | 103 | void transition(Control& control, Context&) { 104 | control.changeTo(); 105 | } 106 | }; 107 | 108 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 109 | 110 | struct YellowUpwards 111 | : M::Base 112 | { 113 | void enter(Context&) { 114 | std::cout << " Yellow ^" << std::endl; 115 | } 116 | 117 | void transition(Control& control, Context&) { 118 | control.changeTo(); 119 | } 120 | }; 121 | 122 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 123 | 124 | struct Green 125 | : M::Base 126 | { 127 | void enter(Context&) { 128 | std::cout << " Green" << std::endl; 129 | } 130 | 131 | void transition(Control& control, Context&) { 132 | control.changeTo(); 133 | } 134 | }; 135 | 136 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 137 | }; 138 | 139 | //------------------------------------------------------------------------------ 140 | 141 | // another top-level state 142 | struct Off 143 | : M::Base 144 | { 145 | void enter(Context&) { 146 | std::cout << "Off" << std::endl; 147 | } 148 | }; 149 | 150 | //////////////////////////////////////////////////////////////////////////////// 151 | 152 | int 153 | main() { 154 | // shared data storage instance 155 | Context context; 156 | 157 | // state machine structure 158 | M::PeerRoot< 159 | // sub-machine .. 160 | M::Composite, 167 | Off 168 | > machine(context); 169 | 170 | while (machine.isActive() == false) 171 | machine.update(); 172 | 173 | return 0; 174 | } 175 | 176 | //////////////////////////////////////////////////////////////////////////////// 177 | -------------------------------------------------------------------------------- /include/hfsm/detail/hash_table.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | namespace detail { 3 | 4 | //////////////////////////////////////////////////////////////////////////////// 5 | 6 | template 7 | HashTable::Item::Item(const Hash hash, const Key key) 8 | : _hash(hash | USED_MASK) 9 | , _keyWrap(key) 10 | {} 11 | 12 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 13 | 14 | template 15 | HashTable::Item::Item(const Hash hash, const Key key, const Value value) 16 | : _hash(hash | USED_MASK) 17 | , _keyWrap(key) 18 | , _value(value) 19 | {} 20 | 21 | //------------------------------------------------------------------------------ 22 | 23 | template 24 | void 25 | HashTable::Item::swap(Item& other) { 26 | std::swap(_hash, other._hash); 27 | std::swap(_keyWrap, other._keyWrap); 28 | std::swap(_value, other._value); 29 | } 30 | 31 | //------------------------------------------------------------------------------ 32 | 33 | template 34 | bool 35 | HashTable::Item::operator == (const Item& other) const { 36 | return _hash == other._hash && *_keyWrap == *other._keyWrap; 37 | } 38 | 39 | //////////////////////////////////////////////////////////////////////////////// 40 | 41 | template 42 | bool 43 | HashTable::insert(const Key key, const Value value) { 44 | assert(_count < CAPACITY); 45 | 46 | if (_count < CAPACITY) { 47 | Item newcomer(hash(key), key, value); 48 | 49 | for (unsigned i = index(newcomer.hash()), newcomerDistance = 0; 50 | ; 51 | i = index(i + 1), ++newcomerDistance) 52 | { 53 | Item& resident = _items[i]; 54 | 55 | if (resident.occupied()) { 56 | const unsigned residentDistance = probeCount(i); 57 | 58 | if (newcomerDistance > residentDistance) { 59 | newcomerDistance = residentDistance; 60 | 61 | std::swap(newcomer, resident); 62 | } 63 | } 64 | else { 65 | resident = newcomer; 66 | ++_count; 67 | 68 | return true; 69 | } 70 | 71 | assert(newcomerDistance < CAPACITY); 72 | } 73 | } 74 | else 75 | return false; 76 | } 77 | 78 | //------------------------------------------------------------------------------ 79 | 80 | template 81 | typename HashTable::Value* 82 | HashTable::find(const Key key) { 83 | const unsigned index = locate(key); 84 | 85 | return index != INVALID ? 86 | _items[index].value() : nullptr; 87 | } 88 | 89 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 90 | 91 | template 92 | const typename HashTable::Value* 93 | HashTable::find(const Key key) const { 94 | const unsigned index = locate(key); 95 | 96 | return index != INVALID ? 97 | _items[index].value() : nullptr; 98 | } 99 | 100 | //------------------------------------------------------------------------------ 101 | 102 | template 103 | unsigned 104 | HashTable::locate(const Key key) const { 105 | const Item item(hash(key), key); 106 | 107 | for (unsigned i = index(item.hash()), distance = 0; 108 | ; 109 | i = index(i + 1), ++distance) 110 | { 111 | const Item& resident = _items[i]; 112 | 113 | if (item == resident) 114 | return i; 115 | else if (distance > probeCount(i)) 116 | return INVALID; 117 | 118 | assert(distance < CAPACITY); 119 | } 120 | } 121 | 122 | //------------------------------------------------------------------------------ 123 | 124 | template 125 | unsigned 126 | HashTable::probeCount(const unsigned i) const { 127 | assert(i < CAPACITY); 128 | 129 | return index(CAPACITY + i - index(_items[i].hash())); 130 | } 131 | 132 | //------------------------------------------------------------------------------ 133 | 134 | template 135 | unsigned 136 | HashTable::skipVacantForward(const unsigned i) const { 137 | assert(0 <= i && i <= CAPACITY); 138 | 139 | if (i < CAPACITY) { 140 | unsigned n = i; 141 | for (; _items[n].vacant(); ++n) 142 | if (n < CAPACITY) 143 | continue; 144 | else 145 | return CAPACITY; 146 | 147 | return n; 148 | } 149 | else 150 | return i; 151 | } 152 | 153 | //////////////////////////////////////////////////////////////////////////////// 154 | 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /projects/visual-studio/HFSM.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2036 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_", "_", "{498EF6B0-5565-411A-839A-B41294202103}" 7 | ProjectSection(SolutionItems) = preProject 8 | ..\..\.gitignore = ..\..\.gitignore 9 | ..\..\.travis.yml = ..\..\.travis.yml 10 | ..\..\appveyor.yml = ..\..\appveyor.yml 11 | ..\..\CMakeLists.txt = ..\..\CMakeLists.txt 12 | ..\..\README.md = ..\..\README.md 13 | EndProjectSection 14 | EndProject 15 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HFSM", "HFSM.vcxproj", "{0895F78B-D3D4-4BEF-8E12-DAABE32686E5}" 16 | EndProject 17 | Project("{888888A0-9F3D-457C-B088-3A5042F75D52}") = "HFSM-tools", "HFSM-tools.pyproj", "{2347027E-E5DE-43C4-8EF9-5F1411ADE33B}" 18 | EndProject 19 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "examples", "examples", "{EDEB0687-3B3E-429D-9213-0A3EED99821D}" 20 | EndProject 21 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "basic_streetlight", "basic_streetlight.vcxproj", "{C543B4D9-A8AC-47B7-8376-AB4B110C144E}" 22 | EndProject 23 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "basic_event", "basic_event.vcxproj", "{AA58BDD0-0F56-43E9-816C-37BA6C30F97C}" 24 | EndProject 25 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "basic_logger_interface", "basic_logger_interface.vcxproj", "{E1C0E44B-EEF3-40D4-AEB7-54C4E570237E}" 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|32 = Debug|32 30 | Debug|64 = Debug|64 31 | Release|32 = Release|32 32 | Release|64 = Release|64 33 | EndGlobalSection 34 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 35 | {0895F78B-D3D4-4BEF-8E12-DAABE32686E5}.Debug|32.ActiveCfg = Debug|Win32 36 | {0895F78B-D3D4-4BEF-8E12-DAABE32686E5}.Debug|32.Build.0 = Debug|Win32 37 | {0895F78B-D3D4-4BEF-8E12-DAABE32686E5}.Debug|64.ActiveCfg = Debug|x64 38 | {0895F78B-D3D4-4BEF-8E12-DAABE32686E5}.Debug|64.Build.0 = Debug|x64 39 | {0895F78B-D3D4-4BEF-8E12-DAABE32686E5}.Release|32.ActiveCfg = Release|Win32 40 | {0895F78B-D3D4-4BEF-8E12-DAABE32686E5}.Release|32.Build.0 = Release|Win32 41 | {0895F78B-D3D4-4BEF-8E12-DAABE32686E5}.Release|64.ActiveCfg = Release|x64 42 | {0895F78B-D3D4-4BEF-8E12-DAABE32686E5}.Release|64.Build.0 = Release|x64 43 | {2347027E-E5DE-43C4-8EF9-5F1411ADE33B}.Debug|32.ActiveCfg = Debug|Any CPU 44 | {2347027E-E5DE-43C4-8EF9-5F1411ADE33B}.Debug|64.ActiveCfg = Debug|Any CPU 45 | {2347027E-E5DE-43C4-8EF9-5F1411ADE33B}.Release|32.ActiveCfg = Release|Any CPU 46 | {2347027E-E5DE-43C4-8EF9-5F1411ADE33B}.Release|64.ActiveCfg = Release|Any CPU 47 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E}.Debug|32.ActiveCfg = Debug|Win32 48 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E}.Debug|32.Build.0 = Debug|Win32 49 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E}.Debug|64.ActiveCfg = Debug|x64 50 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E}.Debug|64.Build.0 = Debug|x64 51 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E}.Release|32.ActiveCfg = Release|Win32 52 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E}.Release|32.Build.0 = Release|Win32 53 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E}.Release|64.ActiveCfg = Release|x64 54 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E}.Release|64.Build.0 = Release|x64 55 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C}.Debug|32.ActiveCfg = Debug|Win32 56 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C}.Debug|32.Build.0 = Debug|Win32 57 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C}.Debug|64.ActiveCfg = Debug|x64 58 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C}.Debug|64.Build.0 = Debug|x64 59 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C}.Release|32.ActiveCfg = Release|Win32 60 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C}.Release|32.Build.0 = Release|Win32 61 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C}.Release|64.ActiveCfg = Release|x64 62 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C}.Release|64.Build.0 = Release|x64 63 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E}.Debug|32.ActiveCfg = Debug|Win32 64 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E}.Debug|32.Build.0 = Debug|Win32 65 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E}.Debug|64.ActiveCfg = Debug|x64 66 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E}.Debug|64.Build.0 = Debug|x64 67 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E}.Release|32.ActiveCfg = Release|Win32 68 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E}.Release|32.Build.0 = Release|Win32 69 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E}.Release|64.ActiveCfg = Release|x64 70 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E}.Release|64.Build.0 = Release|x64 71 | EndGlobalSection 72 | GlobalSection(SolutionProperties) = preSolution 73 | HideSolutionNode = FALSE 74 | EndGlobalSection 75 | GlobalSection(NestedProjects) = preSolution 76 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E} = {EDEB0687-3B3E-429D-9213-0A3EED99821D} 77 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C} = {EDEB0687-3B3E-429D-9213-0A3EED99821D} 78 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E} = {EDEB0687-3B3E-429D-9213-0A3EED99821D} 79 | EndGlobalSection 80 | GlobalSection(ExtensibilityGlobals) = postSolution 81 | SolutionGuid = {3BE666E0-1098-42CF-B1FD-F0F0CA579F4C} 82 | EndGlobalSection 83 | EndGlobal 84 | -------------------------------------------------------------------------------- /examples/debug_logger_interface/main.cpp: -------------------------------------------------------------------------------- 1 | // HFSM (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | // 4 | // Attachable logger example 5 | 6 | // Full output: 7 | // 8 | // --- ctor: --- 9 | // 10 | // Top::enter() 11 | // Top::From::enter() 12 | // 13 | // -- update: -- 14 | // 15 | // Top::update() 16 | // Top::transition() 17 | // Top::From::update() 18 | // Top::From::transition() 19 | // 20 | // -- react: --- 21 | // 22 | // Top::react() 23 | // Top::From::react() 24 | // Top::To::substitute() 25 | // Top::From::leave() 26 | // Top::To::enter() 27 | // 28 | // -- detach: -- 29 | // 30 | // 31 | // --- dtor: --- 32 | // 33 | // Top::To::leave() 34 | // Top::leave() 35 | // 36 | // --- done! --- 37 | 38 | // enable logger functionality 39 | #define HFSM_ENABLE_LOG_INTERFACE 40 | #include 41 | 42 | #include 43 | 44 | //------------------------------------------------------------------------------ 45 | 46 | // data shared between FSM states and outside code 47 | struct Context {}; 48 | 49 | // convenience typedef 50 | using M = hfsm::Machine; 51 | 52 | //////////////////////////////////////////////////////////////////////////////// 53 | 54 | struct Logger 55 | : hfsm::LoggerInterface 56 | { 57 | // hfsm::LoggerInterface 58 | void record(const std::type_index& /*state*/, 59 | const char* const stateName, 60 | const Method /*method*/, 61 | const char* const methodName) override 62 | { 63 | std::cout << stateName << "::" << methodName << "()\n"; 64 | } 65 | }; 66 | 67 | //////////////////////////////////////////////////////////////////////////////// 68 | 69 | // top-level state in the hierarchy 70 | struct Top 71 | : M::Base 72 | { 73 | // all state methods: 74 | void substitute(Control&, Context&) {} // not going to be called in this example 75 | void enter(Context&) {} 76 | void update(Context&) {} 77 | void transition(Control&, Context&) {} 78 | template 79 | void react(const TEvent&, Control&, Context&) {} 80 | void leave(Context&) {} 81 | 82 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 83 | 84 | // forward declared for Red::transition() 85 | struct To; 86 | 87 | // initial state 88 | struct From 89 | : M::Base // necessary boilerplate! 90 | { 91 | // all state methods: 92 | void substitute(Control&, Context&) {} // not going to be called in this example 93 | void enter(Context&) {} 94 | void update(Context&) {} 95 | void transition(Control&, Context&) {} 96 | template 97 | void react(const TEvent&, Control& control, Context&) { control.changeTo(); } 98 | void leave(Context&) {} 99 | }; 100 | 101 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 102 | 103 | // transition target state 104 | struct To 105 | : M::Base 106 | { 107 | // all state methods: 108 | void substitute(Control&, Context&) {} 109 | void enter(Context&) {} 110 | void update(Context&) {} 111 | void transition(Control&, Context&) {} 112 | template 113 | void react(const TEvent&, Control&, Context&) {} // not going to be called in this example 114 | void leave(Context&) {} 115 | }; 116 | 117 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 118 | }; 119 | 120 | //////////////////////////////////////////////////////////////////////////////// 121 | 122 | int main() { 123 | using FSM = M::Root; 127 | 128 | { 129 | // shared data storage instance 130 | Context context; 131 | 132 | // logger 133 | Logger logger; 134 | 135 | std::cout << "--- ctor: ---\n\n"; 136 | 137 | // state machine instance - all initial states are activated 138 | FSM machine(context, &logger); 139 | 140 | // output: 141 | // Top::enter() 142 | // Top::From::enter() 143 | 144 | std::cout << "\n-- update: --\n\n"; 145 | 146 | // first update 147 | machine.update(); 148 | 149 | // output: 150 | // Top::update() 151 | // Top::transition() 152 | // Top::From::update() 153 | // Top::From::transition() 154 | 155 | std::cout << "\n-- react: ---\n\n"; 156 | 157 | machine.react(1); 158 | 159 | // output: 160 | // Top::react() 161 | // Top::From::react() 162 | // Top::To::substitute() 163 | // Top::From::leave() 164 | // Top::To::enter() 165 | 166 | std::cout << "\n-- detach: --\n\n"; 167 | 168 | // detach logger and update again 169 | machine.attachLogger(nullptr); 170 | machine.update(); 171 | 172 | // no output, since logger is detached 173 | 174 | std::cout << "\n--- dtor: ---\n\n"; 175 | 176 | // re-attach logger for destruction log 177 | machine.attachLogger(&logger); 178 | 179 | // state machine instance gets destroyed 180 | } 181 | 182 | // output: 183 | // Top::To::leave() 184 | // Top::leave() 185 | 186 | std::cout << "\n--- done! ---\n\n"; 187 | 188 | return 0; 189 | } 190 | 191 | //////////////////////////////////////////////////////////////////////////////// 192 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | #sudo: false 2 | 3 | language: cpp 4 | 5 | notifications: 6 | email: true 7 | 8 | os: linux 9 | dist: trusty 10 | 11 | env: 12 | global: 13 | - TMPDIR=/tmp 14 | 15 | matrix: 16 | include: 17 | 18 | # GCC 4.9 19 | - env: COMPILER=g++-4.9 BUILD_TYPE=Debug 20 | addons: &gcc49 21 | apt: 22 | packages: g++-4.9 23 | sources: ubuntu-toolchain-r-test 24 | 25 | - env: COMPILER=g++-4.9 BUILD_TYPE=Release 26 | addons: *gcc49 27 | 28 | # GCC 5 29 | - env: COMPILER=g++-5 BUILD_TYPE=Debug 30 | addons: &gcc5 31 | apt: 32 | packages: g++-5 33 | sources: ubuntu-toolchain-r-test 34 | 35 | - env: COMPILER=g++-5 BUILD_TYPE=Release 36 | addons: *gcc5 37 | 38 | # GCC 6 39 | - env: COMPILER=g++-6 BUILD_TYPE=Debug 40 | addons: &gcc6 41 | apt: 42 | packages: g++-6 43 | sources: ubuntu-toolchain-r-test 44 | 45 | - env: COMPILER=g++-6 BUILD_TYPE=Release 46 | addons: *gcc6 47 | 48 | # GCC 7 49 | - env: COMPILER=g++-7 BUILD_TYPE=Debug 50 | addons: &gcc7 51 | apt: 52 | packages: g++-7 53 | sources: ubuntu-toolchain-r-test 54 | 55 | - env: COMPILER=g++-7 BUILD_TYPE=Release 56 | addons: *gcc7 57 | 58 | # GCC 8 59 | - env: COMPILER=g++-8 BUILD_TYPE=Debug 60 | addons: &gcc8 61 | apt: 62 | packages: g++-8 63 | sources: ubuntu-toolchain-r-test 64 | 65 | - env: COMPILER=g++-8 BUILD_TYPE=Release 66 | addons: *gcc8 67 | 68 | # Clang 3.6 69 | - env: COMPILER=clang++-3.6 BUILD_TYPE=Debug 70 | addons: &clang36 71 | apt: 72 | packages: 73 | - clang-3.6 74 | - g++-5 75 | sources: 76 | - ubuntu-toolchain-r-test 77 | - llvm-toolchain-precise-3.6 78 | 79 | - env: COMPILER=clang++-3.6 BUILD_TYPE=Release 80 | addons: *clang36 81 | 82 | # Clang 3.7 83 | - env: COMPILER=clang++-3.7 BUILD_TYPE=Debug 84 | addons: &clang37 85 | apt: 86 | packages: 87 | - clang-3.7 88 | - g++-5 89 | sources: 90 | - ubuntu-toolchain-r-test 91 | - llvm-toolchain-precise-3.7 92 | 93 | - env: COMPILER=clang++-3.7 BUILD_TYPE=Release 94 | addons: *clang37 95 | 96 | # Clang 3.8 97 | - env: COMPILER=clang++-3.8 BUILD_TYPE=Debug 98 | addons: &clang38 99 | apt: 100 | packages: 101 | - clang-3.8 102 | - g++-5 103 | sources: 104 | - ubuntu-toolchain-r-test 105 | - llvm-toolchain-precise-3.8 106 | 107 | - env: COMPILER=clang++-3.8 BUILD_TYPE=Release 108 | addons: *clang38 109 | 110 | # Clang 3.9 111 | - env: COMPILER=clang++-3.9 BUILD_TYPE=Debug 112 | addons: &clang39 113 | apt: 114 | packages: 115 | - clang-3.9 116 | - g++-5 117 | sources: 118 | - ubuntu-toolchain-r-test 119 | - llvm-toolchain-precise-3.9 120 | 121 | - env: COMPILER=clang++-3.9 BUILD_TYPE=Release 122 | addons: *clang39 123 | 124 | # Clang 4.0 125 | - env: COMPILER=clang++-4.0 BUILD_TYPE=Debug 126 | addons: &clang4 127 | apt: 128 | packages: 129 | - clang-4.0 130 | - g++-5 131 | sources: 132 | - ubuntu-toolchain-r-test 133 | - llvm-toolchain-trusty-4.0 134 | 135 | - env: COMPILER=clang++-4.0 BUILD_TYPE=Release 136 | addons: *clang4 137 | 138 | # Clang 5.0 139 | - env: COMPILER=clang++-5.0 BUILD_TYPE=Debug 140 | addons: &clang5 141 | apt: 142 | packages: 143 | - clang-5.0 144 | - g++-5 145 | sources: 146 | - ubuntu-toolchain-r-test 147 | - llvm-toolchain-trusty-5.0 148 | 149 | - env: COMPILER=clang++-5.0 BUILD_TYPE=Release 150 | addons: *clang5 151 | 152 | # Clang 6.0 153 | - env: COMPILER=clang++-6.0 BUILD_TYPE=Debug 154 | addons: &clang6 155 | apt: 156 | packages: 157 | - clang-6.0 158 | - g++-6 159 | sources: 160 | - ubuntu-toolchain-r-test 161 | - llvm-toolchain-trusty-6.0 162 | 163 | - env: COMPILER=clang++-6.0 BUILD_TYPE=Release 164 | addons: *clang6 165 | 166 | script: 167 | - cd "${TRAVIS_BUILD_DIR}" 168 | - mkdir build && cd build 169 | - cmake .. -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_INSTALL_PREFIX=$TMPDIR/hfsm 170 | - make install 171 | 172 | - cd "${TRAVIS_BUILD_DIR}/examples/advanced_event_handling" 173 | - mkdir build && cd build 174 | - cmake .. -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_BUILD_TYPE=$BUILD_TYPE 175 | - make 176 | 177 | - cd "${TRAVIS_BUILD_DIR}/examples/basic_streetlight" 178 | - mkdir build && cd build 179 | - cmake .. -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_BUILD_TYPE=$BUILD_TYPE 180 | - make 181 | 182 | - cd "${TRAVIS_BUILD_DIR}/examples/debug_logger_interface" 183 | - mkdir build && cd build 184 | - cmake .. -DCMAKE_CXX_COMPILER=$COMPILER -DCMAKE_BUILD_TYPE=$BUILD_TYPE 185 | - make 186 | -------------------------------------------------------------------------------- /examples/advanced_event_handling/main.cpp: -------------------------------------------------------------------------------- 1 | // HFSM (hierarchical state machine for games and interactive applications) 2 | // Created by Andrew Gresyk 3 | // 4 | // Event handling example 5 | 6 | // Output: 7 | // 8 | // sending PrimaryEvent: 9 | // ConcreteHandler: reacting to PrimaryEvent 10 | // TemplateHandler: reacting to TEvent 11 | // EnableIfHandler: reacting to a 12 | // 13 | // sending SecondaryEvent: 14 | // ConcreteHandler: reacting to SecondaryEvent 15 | // TemplateHandler: reacting to TEvent 16 | // EnableIfHandler: reacting to a 17 | // 18 | // sending TransitionEvent: 19 | // Reactive: reacting to TransitionEvent (aka 'char') 20 | // TemplateHandler: reacting to TEvent 21 | // EnableIfHandler: reacting to a 22 | // changed to Target 23 | 24 | // optional: enable FSM structure report in debugger 25 | #define HFSM_ENABLE_STRUCTURE_REPORT 26 | 27 | #include 28 | 29 | #include 30 | 31 | //------------------------------------------------------------------------------ 32 | 33 | struct Context {}; 34 | 35 | using M = hfsm::Machine; 36 | 37 | struct PrimaryEvent {}; 38 | struct SecondaryEvent { int payload; }; 39 | 40 | using TransitionEvent = char; 41 | 42 | //////////////////////////////////////////////////////////////////////////////// 43 | 44 | // forward declared for Reactive::transition() 45 | struct Target; 46 | 47 | struct Reactive 48 | : M::Base 49 | { 50 | // handle a single event type - TransitionEvent 51 | void react(const TransitionEvent&, Control& control, Context&) { 52 | std::cout << " Reactive: reacting to TransitionEvent\n"; 53 | 54 | control.changeTo(); 55 | } 56 | 57 | // and ignore the other event types 58 | using M::Base::react; 59 | 60 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 61 | 62 | struct NonHandler 63 | : M::Base 64 | { 65 | // events are totally opt-in 66 | }; 67 | 68 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 69 | 70 | struct ConcreteHandler 71 | : M::Base 72 | { 73 | // handle two event types - PrimaryEvent 74 | void react(const PrimaryEvent&, Control&, Context&) { 75 | std::cout << " ConcreteHandler: reacting to PrimaryEvent\n"; 76 | } 77 | 78 | // and SecondaryEvent 79 | void react(const SecondaryEvent&, Control&, Context&) { 80 | std::cout << " ConcreteHandler: reacting to SecondaryEvent\n"; 81 | } 82 | 83 | // and ignore the other event types 84 | using M::Base::react; 85 | }; 86 | 87 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 88 | 89 | struct TemplateHandler 90 | : M::Base 91 | { 92 | // handle all possible event types 93 | template 94 | void react(const TEvent&, Control&, Context&) { 95 | std::cout << " TemplateHandler: reacting to TEvent\n"; 96 | } 97 | }; 98 | 99 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 100 | 101 | struct EnableIfHandler 102 | : M::Base 103 | { 104 | // use std::enable_if to build more complex conditional event handling 105 | template 106 | typename std::enable_if::value>::type 107 | react(const TEvent&, Control&, Context&) { 108 | std::cout << " EnableIfHandler: reacting to a \n"; 109 | } 110 | 111 | // but remember to cover all the remaining cases 112 | template 113 | typename std::enable_if::value>::type 114 | react(const TEvent&, Control&, Context&) { 115 | std::cout << " EnableIfHandler: reacting to a \n"; 116 | } 117 | }; 118 | 119 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 120 | }; 121 | 122 | //------------------------------------------------------------------------------ 123 | 124 | struct Target 125 | : M::Base 126 | { 127 | void enter(Context&) { 128 | std::cout << " changed to Target\n"; 129 | } 130 | }; 131 | 132 | //////////////////////////////////////////////////////////////////////////////// 133 | 134 | int 135 | main() { 136 | Context context; 137 | 138 | M::PeerRoot< 139 | M::Orthogonal, 145 | Target 146 | > machine(context); 147 | 148 | std::cout << "sending PrimaryEvent:\n"; 149 | machine.react(PrimaryEvent{}); 150 | 151 | // output: 152 | // ConcreteHandler: reacting to PrimaryEvent 153 | // TemplateHandler: reacting to TEvent 154 | // EnableIfHandler: reacting to a 155 | 156 | std::cout << "\nsending SecondaryEvent:\n"; 157 | machine.react(SecondaryEvent{}); 158 | 159 | // output: 160 | // ConcreteHandler: reacting to SecondaryEvent 161 | // TemplateHandler: reacting to TEvent 162 | // EnableIfHandler: reacting to a 163 | 164 | std::cout << "\nsending TransitionEvent (aka 'char'):\n"; 165 | machine.react(TransitionEvent{}); 166 | 167 | // output: 168 | // Reactive: reacting to TransitionEvent (aka 'char') 169 | // TemplateHandler: reacting to TEvent 170 | // EnableIfHandler: reacting to a 171 | // changed to Target 172 | 173 | std::cout<< std::endl; 174 | return 0; 175 | }; 176 | 177 | //////////////////////////////////////////////////////////////////////////////// 178 | -------------------------------------------------------------------------------- /hfsm.natvis: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | {_Tptr->_Data._UndecoratedName,sb} 9 | 10 | {&_Tptr->_Data._DecoratedName[6],[strchr(_Tptr->_Data._DecoratedName,'@') - _Tptr->_Data._DecoratedName - 6]sb} 11 | < 12 | {strstr(_Tptr->_Data._DecoratedName,"@V") + 2,[strstr(_Tptr->_Data._DecoratedName,"@?") - strstr(_Tptr->_Data._DecoratedName,"@V") - 2]sb} 13 | > 14 | 15 | {&_Tptr->_Data._DecoratedName[4],[strchr(_Tptr->_Data._DecoratedName,'@') - _Tptr->_Data._DecoratedName - 4]sb} 16 | 17 | 18 | 19 | 20 | 21 | 22 | {_count} / {_capacity} 23 | 24 | 25 | _count 26 | *(($T1*) &((Item*) (&_count + 1))[$i]) 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | {_count} / {_capacity} 35 | 36 | 37 | _count 38 | *(($T1*) &_storage[$i]) 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | {_count} 47 | 48 | 49 | $T3 50 | _items[$i],na 51 | 52 | 53 | 54 | 55 | 56 | {_keyWrap} : {_value} 57 | 58 | _keyWrap 59 | _value 60 | 61 | 62 | 63 | 64 | 65 | 66 | {*($T1*) &_storage._Val} 67 | 68 | *($T1*) &_storage._Val 69 | 70 | 71 | 72 | 73 | 74 | 75 | █ {prefix,sub}{name,sb} 76 | ░ {prefix,sub}{name,sb} 77 | 78 | 79 | 80 | 81 | 82 | 83 | {forkType} ► {prongType} 84 | 85 | 86 | 87 | 88 | 89 | 90 | {_typeToIndex,na} 91 | 92 | _typeToIndex,na 93 | 94 | 95 | 96 | 97 | 98 | 99 | {type}: {*(std::type_index*)&stateType._storage} 100 | 101 | 102 | 103 | 104 | 105 | 106 | [{type}] <{resumableType,na}> ◄ <{activeType,na}> ► <{requestedType,na}> 107 | 108 | type 109 | activeType 110 | resumableType 111 | requestedType 112 | 113 | 114 | 115 | 116 | 117 | 118 | {_type} 119 | 120 | _client 121 | 122 | 123 | 124 | 125 | 126 | 127 | {_fork} 128 | 129 | _state 130 | _subStates 131 | 132 | 133 | 134 | 135 | {initial} {remaining} 136 | 137 | 138 | 139 | {initial} 140 | 141 | 142 | 143 | 144 | 145 | {_type} 146 | 147 | _state 148 | _subStates 149 | 150 | 151 | 152 | 153 | {initial} {remaining} 154 | 155 | 156 | 157 | 158 | 159 | {_structure} 160 | 161 | {_structure} 162 | 163 | 164 | 165 | 166 | 167 | 168 | [{_cursor}] = {_t[_cursor]} 169 | 170 | _container 171 | 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_state_methods.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | M::_S::_S(StateRegistry& stateRegistry, 8 | const Parent parent, 9 | Parents& stateParents, 10 | Parents& /*forkParents*/, 11 | ForkPointers& /*forkPointers*/) 12 | { 13 | const auto id = stateRegistry.add(TypeInfo::get()); 14 | stateParents[id] = parent; 15 | } 16 | 17 | //------------------------------------------------------------------------------ 18 | 19 | template 20 | template 21 | bool 22 | M::_S::deepSubstitute(Control& control, 23 | Context& context, 24 | LoggerInterface* const HFSM_IF_LOGGER(logger)) 25 | { 26 | HFSM_IF_LOGGER(if (logger) log(*logger)); 27 | 28 | const unsigned requestCountBefore = control.requestCount(); 29 | 30 | _head.widePreSubstitute(context); 31 | _head.substitute(control, context); 32 | 33 | return requestCountBefore < control.requestCount(); 34 | } 35 | 36 | //------------------------------------------------------------------------------ 37 | 38 | template 39 | template 40 | void 41 | M::_S::deepEnterInitial(Context& context, 42 | LoggerInterface* const logger) 43 | { 44 | deepEnter(context, logger); 45 | } 46 | 47 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 48 | 49 | template 50 | template 51 | void 52 | M::_S::deepEnter(Context& context, 53 | LoggerInterface* const HFSM_IF_LOGGER(logger)) 54 | { 55 | HFSM_IF_LOGGER(if (logger) log(*logger)); 56 | 57 | _head.widePreEnter(context); 58 | _head.enter(context); 59 | } 60 | 61 | //------------------------------------------------------------------------------ 62 | 63 | template 64 | template 65 | bool 66 | M::_S::deepUpdateAndTransition(Control& control, 67 | Context& context, 68 | LoggerInterface* const HFSM_IF_LOGGER(logger)) 69 | { 70 | HFSM_IF_LOGGER(if (logger) log(*logger)); 71 | 72 | _head.widePreUpdate(context); 73 | _head.update(context); 74 | 75 | HFSM_IF_LOGGER(if (logger) log(*logger)); 76 | 77 | const unsigned requestCountBefore = control.requestCount(); 78 | 79 | _head.widePreTransition(context); 80 | _head.transition(control, context); 81 | 82 | return requestCountBefore < control.requestCount(); 83 | } 84 | 85 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 86 | 87 | template 88 | template 89 | void 90 | M::_S::deepUpdate(Context& context, 91 | LoggerInterface* const HFSM_IF_LOGGER(logger)) 92 | { 93 | HFSM_IF_LOGGER(if (logger) log(*logger)); 94 | 95 | _head.widePreUpdate(context); 96 | _head.update(context); 97 | } 98 | 99 | //------------------------------------------------------------------------------ 100 | 101 | template 102 | template 103 | template 104 | void 105 | M::_S::deepReact(const TEvent& event, 106 | Control& control, 107 | Context& context, 108 | LoggerInterface* const HFSM_IF_LOGGER(logger)) 109 | { 110 | HFSM_IF_LOGGER(if (logger) log), LoggerInterface::Method::React>(*logger)); 111 | 112 | _head.widePreReact(event, context); 113 | _head.react(event, control, context); 114 | } 115 | 116 | //------------------------------------------------------------------------------ 117 | 118 | template 119 | template 120 | void 121 | M::_S::deepLeave(Context& context, 122 | LoggerInterface* const HFSM_IF_LOGGER(logger)) 123 | { 124 | HFSM_IF_LOGGER(if (logger) log(*logger)); 125 | 126 | _head.leave(context); 127 | _head.widePostLeave(context); 128 | } 129 | 130 | //------------------------------------------------------------------------------ 131 | 132 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 133 | 134 | template 135 | template 136 | const char* 137 | M::_S::name() { 138 | if (isBare()) 139 | return ""; 140 | else { 141 | const char* const raw = TypeInfo::get()->name(); 142 | 143 | #if defined(_MSC_VER) 144 | 145 | unsigned first = 146 | raw[0] == 's' ? 7 : // Struct 147 | raw[0] == 'c' ? 6 : // Class 148 | 0; 149 | 150 | for (auto c = first; raw[c]; ++c) 151 | if (raw[c] == ':') 152 | first = c + 1; 153 | 154 | return raw + first; 155 | 156 | #elif defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) 157 | 158 | return raw; 159 | 160 | #else 161 | 162 | return raw; 163 | 164 | #endif 165 | } 166 | } 167 | 168 | //------------------------------------------------------------------------------ 169 | 170 | template 171 | template 172 | void 173 | M::_S::deepGetNames(const unsigned parent, 174 | const enum StateInfo::RegionType region, 175 | const unsigned depth, 176 | StateInfos& _stateInfos) const 177 | { 178 | _stateInfos << StateInfo { parent, region, depth, name() }; 179 | } 180 | 181 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 182 | 183 | template 184 | template 185 | void 186 | M::_S::deepIsActive(const bool isActive, 187 | unsigned& index, 188 | MachineStructure& structure) const 189 | { 190 | if (!isBare()) 191 | structure[index++].isActive = isActive; 192 | } 193 | 194 | #endif 195 | 196 | //------------------------------------------------------------------------------ 197 | 198 | #ifdef HFSM_ENABLE_LOG_INTERFACE 199 | 200 | template 201 | template 202 | const char* 203 | M::_S::fullName() { 204 | if (isBare()) 205 | return ""; 206 | else { 207 | const char* const raw = TypeInfo::get()->name(); 208 | 209 | #if defined(_MSC_VER) 210 | 211 | unsigned first = 212 | raw[0] == 's' ? 7 : // Struct 213 | raw[0] == 'c' ? 6 : // Class 214 | 0; 215 | return raw + first; 216 | 217 | #elif defined(__clang__) || defined(__GNUC__) || defined(__GNUG__) 218 | 219 | return raw; 220 | 221 | #else 222 | 223 | return raw; 224 | 225 | #endif 226 | } 227 | } 228 | 229 | #endif 230 | 231 | //////////////////////////////////////////////////////////////////////////////// 232 | 233 | } 234 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_streetlight.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 15.0 29 | {C543B4D9-A8AC-47B7-8376-AB4B110C144E} 30 | Win32Proj 31 | 8.1 32 | 33 | 34 | 35 | Application 36 | true 37 | v140 38 | Unicode 39 | 40 | 41 | Application 42 | false 43 | v140 44 | true 45 | Unicode 46 | 47 | 48 | Application 49 | true 50 | v140 51 | Unicode 52 | 53 | 54 | Application 55 | false 56 | v140 57 | true 58 | Unicode 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | true 84 | 85 | 86 | false 87 | 88 | 89 | true 90 | 91 | 92 | false 93 | 94 | 95 | 96 | 97 | 98 | Disabled 99 | WIN32;_DEBUG;%(PreprocessorDefinitions) 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | MaxSpeed 108 | true 109 | true 110 | WIN32;NDEBUG;%(PreprocessorDefinitions) 111 | 112 | 113 | true 114 | true 115 | 116 | 117 | 118 | 119 | 120 | 121 | Disabled 122 | _DEBUG;%(PreprocessorDefinitions) 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | MaxSpeed 131 | true 132 | true 133 | NDEBUG;%(PreprocessorDefinitions) 134 | 135 | 136 | true 137 | true 138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_event.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 15.0 29 | {AA58BDD0-0F56-43E9-816C-37BA6C30F97C} 30 | Win32Proj 31 | 8.1 32 | advanced_event_handling 33 | 34 | 35 | 36 | Application 37 | true 38 | v140 39 | Unicode 40 | 41 | 42 | Application 43 | false 44 | v140 45 | true 46 | Unicode 47 | 48 | 49 | Application 50 | true 51 | v140 52 | Unicode 53 | 54 | 55 | Application 56 | false 57 | v140 58 | true 59 | Unicode 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | true 85 | 86 | 87 | false 88 | 89 | 90 | true 91 | 92 | 93 | false 94 | 95 | 96 | 97 | 98 | 99 | Disabled 100 | WIN32;_DEBUG;%(PreprocessorDefinitions) 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | MaxSpeed 109 | true 110 | true 111 | WIN32;NDEBUG;%(PreprocessorDefinitions) 112 | 113 | 114 | true 115 | true 116 | 117 | 118 | 119 | 120 | 121 | 122 | Disabled 123 | _DEBUG;%(PreprocessorDefinitions) 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | MaxSpeed 132 | true 133 | true 134 | NDEBUG;%(PreprocessorDefinitions) 135 | 136 | 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /projects/visual-studio/basic_logger_interface.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 15.0 29 | {E1C0E44B-EEF3-40D4-AEB7-54C4E570237E} 30 | Win32Proj 31 | 8.1 32 | debug_logger_interface 33 | 34 | 35 | 36 | Application 37 | true 38 | v140 39 | Unicode 40 | 41 | 42 | Application 43 | false 44 | v140 45 | true 46 | Unicode 47 | 48 | 49 | Application 50 | true 51 | v140 52 | Unicode 53 | 54 | 55 | Application 56 | false 57 | v140 58 | true 59 | Unicode 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | true 85 | 86 | 87 | false 88 | 89 | 90 | true 91 | 92 | 93 | false 94 | 95 | 96 | 97 | 98 | 99 | Disabled 100 | WIN32;_DEBUG;%(PreprocessorDefinitions) 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | MaxSpeed 109 | true 110 | true 111 | WIN32;NDEBUG;%(PreprocessorDefinitions) 112 | 113 | 114 | true 115 | true 116 | 117 | 118 | 119 | 120 | 121 | 122 | Disabled 123 | _DEBUG;%(PreprocessorDefinitions) 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | MaxSpeed 132 | true 133 | true 134 | NDEBUG;%(PreprocessorDefinitions) 135 | 136 | 137 | true 138 | true 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_orthogonal_sub_2.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | template 8 | M::_O::Sub::Sub(StateRegistry& stateRegistry, 9 | const Index fork, 10 | Parents& stateParents, 11 | Parents& forkParents, 12 | ForkPointers& forkPointers) 13 | : initial(stateRegistry, 14 | Parent(fork, 15 | ProngIndex 16 | HSFM_IF_DEBUG(, TypeInfo::get()) 17 | HSFM_IF_DEBUG(, TypeInfo::get())), 18 | stateParents, 19 | forkParents, 20 | forkPointers) 21 | {} 22 | 23 | //------------------------------------------------------------------------------ 24 | 25 | template 26 | template 27 | template 28 | void 29 | M::_O::Sub::wideForwardSubstitute(const unsigned HSFM_IF_ASSERT(prong), 30 | Control& control, 31 | Context& context, 32 | LoggerInterface* const logger) 33 | { 34 | assert(prong == ProngIndex); 35 | 36 | initial.deepForwardSubstitute(control, context, logger); 37 | } 38 | 39 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 40 | 41 | template 42 | template 43 | template 44 | void 45 | M::_O::Sub::wideForwardSubstitute(Control& control, 46 | Context& context, 47 | LoggerInterface* const logger) 48 | { 49 | initial.deepForwardSubstitute(control, context, logger); 50 | } 51 | 52 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 53 | 54 | template 55 | template 56 | template 57 | void 58 | M::_O::Sub::wideSubstitute(Control& control, 59 | Context& context, 60 | LoggerInterface* const logger) 61 | { 62 | initial.deepSubstitute(control, context, logger); 63 | } 64 | 65 | //------------------------------------------------------------------------------ 66 | 67 | template 68 | template 69 | template 70 | void 71 | M::_O::Sub::wideEnterInitial(Context& context, 72 | LoggerInterface* const logger) 73 | { 74 | initial.deepEnterInitial(context, logger); 75 | } 76 | 77 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 78 | 79 | template 80 | template 81 | template 82 | void 83 | M::_O::Sub::wideEnter(Context& context, 84 | LoggerInterface* const logger) 85 | { 86 | initial.deepEnter(context, logger); 87 | } 88 | 89 | //------------------------------------------------------------------------------ 90 | 91 | template 92 | template 93 | template 94 | bool 95 | M::_O::Sub::wideUpdateAndTransition(Control& control, 96 | Context& context, 97 | LoggerInterface* const logger) 98 | { 99 | return initial.deepUpdateAndTransition(control, context, logger); 100 | } 101 | 102 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 103 | 104 | template 105 | template 106 | template 107 | void 108 | M::_O::Sub::wideUpdate(Context& context, 109 | LoggerInterface* const logger) 110 | { 111 | initial.deepUpdate(context, logger); 112 | } 113 | 114 | //------------------------------------------------------------------------------ 115 | 116 | template 117 | template 118 | template 119 | template 120 | void 121 | M::_O::Sub::wideReact(const TEvent& event, 122 | Control& control, 123 | Context& context, 124 | LoggerInterface* const logger) 125 | { 126 | initial.deepReact(event, control, context, logger); 127 | } 128 | 129 | //------------------------------------------------------------------------------ 130 | 131 | template 132 | template 133 | template 134 | void 135 | M::_O::Sub::wideLeave(Context& context, 136 | LoggerInterface* const logger) 137 | { 138 | initial.deepLeave(context, logger); 139 | } 140 | 141 | //------------------------------------------------------------------------------ 142 | 143 | template 144 | template 145 | template 146 | void 147 | M::_O::Sub::wideForwardRequest(const unsigned prong, 148 | const enum Transition::Type transition) 149 | { 150 | assert(prong <= ProngIndex); 151 | 152 | if (prong == ProngIndex) 153 | initial.deepForwardRequest(transition); 154 | else 155 | initial.deepForwardRequest(Transition::Remain); 156 | } 157 | 158 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 159 | 160 | template 161 | template 162 | template 163 | void 164 | M::_O::Sub::wideRequestRemain() { 165 | initial.deepRequestRemain(); 166 | } 167 | 168 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 169 | 170 | template 171 | template 172 | template 173 | void 174 | M::_O::Sub::wideRequestRestart() { 175 | initial.deepRequestRestart(); 176 | } 177 | 178 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 179 | 180 | template 181 | template 182 | template 183 | void 184 | M::_O::Sub::wideRequestResume() { 185 | initial.deepRequestResume(); 186 | } 187 | 188 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 189 | 190 | template 191 | template 192 | template 193 | void 194 | M::_O::Sub::wideChangeToRequested(Context& context, 195 | LoggerInterface* const logger) 196 | { 197 | initial.deepChangeToRequested(context, logger); 198 | } 199 | 200 | //------------------------------------------------------------------------------ 201 | 202 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 203 | 204 | template 205 | template 206 | template 207 | void 208 | M::_O::Sub::wideGetNames(const unsigned parent, 209 | const unsigned depth, 210 | StateInfos& _stateInfos) const 211 | { 212 | initial.deepGetNames(parent, StateInfo::Orthogonal, depth, _stateInfos); 213 | } 214 | 215 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 216 | 217 | template 218 | template 219 | template 220 | void 221 | M::_O::Sub::wideIsActive(const bool isActive, 222 | unsigned& index, 223 | MachineStructure& structure) const 224 | { 225 | initial.deepIsActive(isActive, index, structure); 226 | } 227 | 228 | #endif 229 | 230 | //////////////////////////////////////////////////////////////////////////////// 231 | 232 | } 233 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_orthogonal.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | struct M::_O final { 8 | using Head = TH; 9 | using Fork = ForkT; 10 | using State = _S; 11 | 12 | //---------------------------------------------------------------------- 13 | 14 | template 15 | struct Sub; 16 | 17 | //---------------------------------------------------------------------- 18 | 19 | template 20 | struct Sub { 21 | using Initial = typename WrapState::Type; 22 | using Remaining = Sub; 23 | 24 | enum : unsigned { 25 | ProngIndex = TN, 26 | ReverseDepth = detail::Max::Value, 27 | DeepWidth = Initial::DeepWidth + Remaining::DeepWidth, 28 | StateCount = Initial::StateCount + Remaining::StateCount, 29 | ForkCount = Initial::ForkCount + Remaining::ForkCount, 30 | ProngCount = Initial::ProngCount + Remaining::ProngCount, 31 | }; 32 | 33 | Sub(StateRegistry& stateRegistry, 34 | const Index fork, 35 | Parents& stateParents, 36 | Parents& forkParents, 37 | ForkPointers& forkPointers); 38 | 39 | inline void wideForwardSubstitute (const unsigned prong, 40 | Control& control, Context& context, LoggerInterface* const logger); 41 | 42 | inline void wideForwardSubstitute (Control& control, Context& context, LoggerInterface* const logger); 43 | inline void wideSubstitute (Control& control, Context& context, LoggerInterface* const logger); 44 | 45 | inline void wideEnterInitial ( Context& context, LoggerInterface* const logger); 46 | inline void wideEnter ( Context& context, LoggerInterface* const logger); 47 | 48 | inline bool wideUpdateAndTransition (Control& control, Context& context, LoggerInterface* const logger); 49 | inline void wideUpdate ( Context& context, LoggerInterface* const logger); 50 | 51 | template 52 | inline void wideReact (const TEvent& event, 53 | Control& control, Context& context, LoggerInterface* const logger); 54 | 55 | inline void wideLeave ( Context& context, LoggerInterface* const logger); 56 | 57 | inline void wideForwardRequest(const unsigned prong, const enum Transition::Type transition); 58 | inline void wideRequestRemain(); 59 | inline void wideRequestRestart(); 60 | inline void wideRequestResume(); 61 | inline void wideChangeToRequested ( Context& context, LoggerInterface* const logger); 62 | 63 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 64 | enum : unsigned { 65 | NameCount = Initial::NameCount + Remaining::NameCount, 66 | }; 67 | 68 | void wideGetNames(const unsigned parent, 69 | const unsigned depth, 70 | StateInfos& stateInfos) const; 71 | 72 | void wideIsActive(const bool active, 73 | unsigned& index, 74 | MachineStructure& structure) const; 75 | #endif 76 | 77 | Initial initial; 78 | Remaining remaining; 79 | }; 80 | 81 | //---------------------------------------------------------------------- 82 | 83 | template 84 | struct Sub { 85 | using Initial = typename WrapState::Type; 86 | 87 | enum : unsigned { 88 | ProngIndex = TN, 89 | ReverseDepth = Initial::ReverseDepth, 90 | DeepWidth = Initial::DeepWidth, 91 | StateCount = Initial::StateCount, 92 | ForkCount = Initial::ForkCount, 93 | ProngCount = Initial::ProngCount, 94 | }; 95 | 96 | Sub(StateRegistry& stateRegistry, 97 | const Index fork, 98 | Parents& stateParents, 99 | Parents& forkParents, 100 | ForkPointers& forkPointers); 101 | 102 | inline void wideForwardSubstitute (const unsigned prong, 103 | Control& control, Context& context, LoggerInterface* const logger); 104 | 105 | inline void wideForwardSubstitute (Control& control, Context& context, LoggerInterface* const logger); 106 | inline void wideSubstitute (Control& control, Context& context, LoggerInterface* const logger); 107 | 108 | inline void wideEnterInitial ( Context& context, LoggerInterface* const logger); 109 | inline void wideEnter ( Context& context, LoggerInterface* const logger); 110 | 111 | inline bool wideUpdateAndTransition (Control& control, Context& context, LoggerInterface* const logger); 112 | inline void wideUpdate ( Context& context, LoggerInterface* const logger); 113 | 114 | template 115 | inline void wideReact (const TEvent& event, 116 | Control& control, Context& context, LoggerInterface* const logger); 117 | 118 | inline void wideLeave ( Context& context, LoggerInterface* const logger); 119 | 120 | inline void wideForwardRequest(const unsigned prong, const enum Transition::Type transition); 121 | inline void wideRequestRemain(); 122 | inline void wideRequestRestart(); 123 | inline void wideRequestResume(); 124 | inline void wideChangeToRequested ( Context& context, LoggerInterface* const logger); 125 | 126 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 127 | enum : unsigned { 128 | NameCount = Initial::NameCount, 129 | }; 130 | 131 | void wideGetNames(const unsigned parent, 132 | const unsigned depth, 133 | StateInfos& stateInfos) const; 134 | 135 | void wideIsActive(const bool active, 136 | unsigned& index, 137 | MachineStructure& structure) const; 138 | #endif 139 | 140 | Initial initial; 141 | }; 142 | 143 | using SubStates = Sub<0, TS...>; 144 | 145 | //---------------------------------------------------------------------- 146 | 147 | enum : unsigned { 148 | ReverseDepth = SubStates::ReverseDepth + 1, 149 | DeepWidth = SubStates::DeepWidth, 150 | StateCount = State::StateCount + SubStates::StateCount, 151 | ForkCount = SubStates::ForkCount + 1, 152 | ProngCount = SubStates::ProngCount, 153 | Width = sizeof...(TS), 154 | }; 155 | 156 | _O(StateRegistry& stateRegistry, 157 | const Parent parent, 158 | Parents& stateParents, 159 | Parents& forkParents, 160 | ForkPointers& forkPointers); 161 | 162 | inline void deepForwardSubstitute (Control& control, Context& context, LoggerInterface* const logger); 163 | inline void deepSubstitute (Control& control, Context& context, LoggerInterface* const logger); 164 | 165 | inline void deepEnterInitial ( Context& context, LoggerInterface* const logger); 166 | inline void deepEnter ( Context& context, LoggerInterface* const logger); 167 | 168 | inline bool deepUpdateAndTransition (Control& control, Context& context, LoggerInterface* const logger); 169 | inline void deepUpdate ( Context& context, LoggerInterface* const logger); 170 | 171 | template 172 | inline void deepReact (const TEvent& event, 173 | Control& control, Context& context, LoggerInterface* const logger); 174 | 175 | inline void deepLeave ( Context& context, LoggerInterface* const logger); 176 | 177 | inline void deepForwardRequest(const enum Transition::Type transition); 178 | inline void deepRequestRemain(); 179 | inline void deepRequestRestart(); 180 | inline void deepRequestResume(); 181 | inline void deepChangeToRequested ( Context& context, LoggerInterface* const logger); 182 | 183 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 184 | enum : unsigned { 185 | NameCount = State::NameCount + SubStates::NameCount, 186 | }; 187 | 188 | void deepGetNames(const unsigned parent, 189 | const enum StateInfo::RegionType region, 190 | const unsigned depth, 191 | StateInfos& stateInfos) const; 192 | 193 | void deepIsActive(const bool isActive, 194 | unsigned& index, 195 | MachineStructure& structure) const; 196 | #endif 197 | 198 | Fork _fork; 199 | State _state; 200 | SubStates _subStates; 201 | 202 | HSFM_IF_DEBUG(const TypeInfo _type = TypeInfo::get()); 203 | }; 204 | 205 | //////////////////////////////////////////////////////////////////////////////// 206 | 207 | } 208 | 209 | #include "machine_orthogonal_methods.inl" 210 | #include "machine_orthogonal_sub_1.inl" 211 | #include "machine_orthogonal_sub_2.inl" 212 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_composite_sub_2.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | template 8 | M::_C::Sub::Sub(StateRegistry& stateRegistry, 9 | const Index fork, 10 | Parents& stateParents, 11 | Parents& forkParents, 12 | ForkPointers& forkPointers) 13 | : initial(stateRegistry, 14 | Parent(fork, 15 | ProngIndex 16 | HSFM_IF_DEBUG(, TypeInfo::get()) 17 | HSFM_IF_DEBUG(, TypeInfo::get())), 18 | stateParents, 19 | forkParents, 20 | forkPointers) 21 | {} 22 | 23 | //------------------------------------------------------------------------------ 24 | 25 | template 26 | template 27 | template 28 | void 29 | M::_C::Sub::wideForwardSubstitute(const unsigned HSFM_IF_ASSERT(prong), 30 | Control& control, 31 | Context& context, 32 | LoggerInterface* const logger) 33 | { 34 | assert(prong == ProngIndex); 35 | 36 | initial.deepForwardSubstitute(control, context, logger); 37 | } 38 | 39 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 40 | 41 | template 42 | template 43 | template 44 | void 45 | M::_C::Sub::wideSubstitute(const unsigned HSFM_IF_ASSERT(prong), 46 | Control& control, 47 | Context& context, 48 | LoggerInterface* const logger) 49 | { 50 | assert(prong == ProngIndex); 51 | 52 | initial.deepSubstitute(control, context, logger); 53 | } 54 | 55 | //------------------------------------------------------------------------------ 56 | 57 | template 58 | template 59 | template 60 | void 61 | M::_C::Sub::wideEnterInitial(Context& context, 62 | LoggerInterface* const logger) 63 | { 64 | initial.deepEnterInitial(context, logger); 65 | } 66 | 67 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 68 | 69 | template 70 | template 71 | template 72 | void 73 | M::_C::Sub::wideEnter(const unsigned HSFM_IF_ASSERT(prong), 74 | Context& context, 75 | LoggerInterface* const logger) 76 | { 77 | assert(prong == ProngIndex); 78 | 79 | initial.deepEnter(context, logger); 80 | } 81 | 82 | //------------------------------------------------------------------------------ 83 | 84 | template 85 | template 86 | template 87 | bool 88 | M::_C::Sub::wideUpdateAndTransition(const unsigned HSFM_IF_ASSERT(prong), 89 | Control& control, 90 | Context& context, 91 | LoggerInterface* const logger) 92 | { 93 | assert(prong == ProngIndex); 94 | 95 | return initial.deepUpdateAndTransition(control, context, logger); 96 | } 97 | 98 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 99 | 100 | template 101 | template 102 | template 103 | void 104 | M::_C::Sub::wideUpdate(const unsigned HSFM_IF_ASSERT(prong), 105 | Context& context, 106 | LoggerInterface* const logger) 107 | { 108 | assert(prong == ProngIndex); 109 | 110 | initial.deepUpdate(context, logger); 111 | } 112 | 113 | //------------------------------------------------------------------------------ 114 | 115 | template 116 | template 117 | template 118 | template 119 | void 120 | M::_C::Sub::wideReact(const unsigned HSFM_IF_ASSERT(prong), 121 | const TEvent& event, 122 | Control& control, 123 | Context& context, 124 | LoggerInterface* const logger) 125 | { 126 | assert(prong == ProngIndex); 127 | 128 | initial.deepReact(event, control, context, logger); 129 | } 130 | 131 | //------------------------------------------------------------------------------ 132 | 133 | template 134 | template 135 | template 136 | void 137 | M::_C::Sub::wideLeave(const unsigned HSFM_IF_ASSERT(prong), 138 | Context& context, 139 | LoggerInterface* const logger) 140 | { 141 | assert(prong == ProngIndex); 142 | 143 | initial.deepLeave(context, logger); 144 | } 145 | 146 | //------------------------------------------------------------------------------ 147 | 148 | template 149 | template 150 | template 151 | void 152 | M::_C::Sub::wideForwardRequest(const unsigned HSFM_IF_ASSERT(prong), 153 | const enum Transition::Type transition) 154 | { 155 | assert(prong == ProngIndex); 156 | 157 | initial.deepForwardRequest(transition); 158 | } 159 | 160 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 161 | 162 | template 163 | template 164 | template 165 | void 166 | M::_C::Sub::wideRequestRemain() { 167 | initial.deepRequestRemain(); 168 | } 169 | 170 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 171 | 172 | template 173 | template 174 | template 175 | void 176 | M::_C::Sub::wideRequestRestart() { 177 | initial.deepRequestRestart(); 178 | } 179 | 180 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 181 | 182 | template 183 | template 184 | template 185 | void 186 | M::_C::Sub::wideRequestResume(const unsigned HSFM_IF_ASSERT(prong)) { 187 | assert(prong == ProngIndex); 188 | 189 | initial.deepRequestResume(); 190 | } 191 | 192 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 193 | 194 | template 195 | template 196 | template 197 | void 198 | M::_C::Sub::wideChangeToRequested(const unsigned HSFM_IF_ASSERT(prong), 199 | Context& context, 200 | LoggerInterface* const logger) 201 | { 202 | assert(prong == ProngIndex); 203 | 204 | initial.deepChangeToRequested(context, logger); 205 | } 206 | 207 | //------------------------------------------------------------------------------ 208 | 209 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 210 | 211 | template 212 | template 213 | template 214 | void 215 | M::_C::Sub::wideGetNames(const unsigned parent, 216 | const unsigned depth, 217 | StateInfos& _stateInfos) const 218 | { 219 | initial.deepGetNames(parent, StateInfo::Composite, depth, _stateInfos); 220 | } 221 | 222 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 223 | 224 | template 225 | template 226 | template 227 | void 228 | M::_C::Sub::wideIsActive(const unsigned prong, 229 | unsigned& index, 230 | MachineStructure& structure) const 231 | { 232 | initial.deepIsActive(prong == ProngIndex, index, structure); 233 | } 234 | 235 | #endif 236 | 237 | //////////////////////////////////////////////////////////////////////////////// 238 | 239 | } 240 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_composite.hpp: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | struct M::_C final { 8 | using Head = TH; 9 | using Fork = ForkT; 10 | using State = _S; 11 | 12 | //---------------------------------------------------------------------- 13 | 14 | template 15 | struct Sub; 16 | 17 | //---------------------------------------------------------------------- 18 | 19 | template 20 | struct Sub { 21 | using Initial = typename WrapState::Type; 22 | using Remaining = Sub; 23 | 24 | enum : unsigned { 25 | ProngIndex = TN, 26 | ReverseDepth = detail::Max::Value, 27 | DeepWidth = detail::Max::Value, 28 | StateCount = Initial::StateCount + Remaining::StateCount, 29 | ForkCount = Initial::ForkCount + Remaining::ForkCount, 30 | ProngCount = Initial::ProngCount + Remaining::ProngCount, 31 | }; 32 | 33 | Sub(StateRegistry& stateRegistry, 34 | const Index fork, 35 | Parents& stateParents, 36 | Parents& forkParents, 37 | ForkPointers& forkPointers); 38 | 39 | inline void wideForwardSubstitute (const unsigned prong, Control& control, Context& context, LoggerInterface* const logger); 40 | inline void wideSubstitute (const unsigned prong, Control& control, Context& context, LoggerInterface* const logger); 41 | 42 | inline void wideEnterInitial ( Context& context, LoggerInterface* const logger); 43 | inline void wideEnter (const unsigned prong, Context& context, LoggerInterface* const logger); 44 | 45 | inline bool wideUpdateAndTransition (const unsigned prong, Control& control, Context& context, LoggerInterface* const logger); 46 | inline void wideUpdate (const unsigned prong, Context& context, LoggerInterface* const logger); 47 | 48 | template 49 | inline void wideReact (const unsigned prong, 50 | const TEvent& event, Control& control, Context& context, LoggerInterface* const logger); 51 | 52 | inline void wideLeave (const unsigned prong, Context& context, LoggerInterface* const logger); 53 | 54 | inline void wideForwardRequest(const unsigned prong, const enum Transition::Type transition); 55 | inline void wideRequestRemain(); 56 | inline void wideRequestRestart(); 57 | inline void wideRequestResume(const unsigned prong); 58 | inline void wideChangeToRequested (const unsigned prong, Context& context, LoggerInterface* const logger); 59 | 60 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 61 | enum : unsigned { 62 | NameCount = Initial::NameCount + Remaining::NameCount, 63 | }; 64 | 65 | void wideGetNames(const unsigned parent, 66 | const unsigned depth, 67 | StateInfos& stateInfos) const; 68 | 69 | void wideIsActive(const unsigned prong, 70 | unsigned& index, 71 | MachineStructure& structure) const; 72 | #endif 73 | 74 | Initial initial; 75 | Remaining remaining; 76 | }; 77 | 78 | //---------------------------------------------------------------------- 79 | 80 | template 81 | struct Sub { 82 | using Initial = typename WrapState::Type; 83 | 84 | enum : unsigned { 85 | ProngIndex = TN, 86 | ReverseDepth = Initial::ReverseDepth, 87 | DeepWidth = detail::Max<1, Initial::DeepWidth>::Value, 88 | StateCount = Initial::StateCount, 89 | ForkCount = Initial::ForkCount, 90 | ProngCount = Initial::ProngCount, 91 | }; 92 | 93 | Sub(StateRegistry& stateRegistry, 94 | const Index fork, 95 | Parents& stateParents, 96 | Parents& forkParents, 97 | ForkPointers& forkPointers); 98 | 99 | inline void wideForwardSubstitute (const unsigned prong, Control& control, Context& context, LoggerInterface* const logger); 100 | inline void wideSubstitute (const unsigned prong, Control& control, Context& context, LoggerInterface* const logger); 101 | 102 | inline void wideEnterInitial ( Context& context, LoggerInterface* const logger); 103 | inline void wideEnter (const unsigned prong, Context& context, LoggerInterface* const logger); 104 | 105 | inline bool wideUpdateAndTransition (const unsigned prong, Control& control, Context& context, LoggerInterface* const logger); 106 | inline void wideUpdate (const unsigned prong, Context& context, LoggerInterface* const logger); 107 | 108 | template 109 | inline void wideReact (const unsigned prong, const TEvent& event, 110 | Control& control, Context& context, LoggerInterface* const logger); 111 | 112 | inline void wideLeave (const unsigned prong, Context& context, LoggerInterface* const logger); 113 | 114 | inline void wideForwardRequest(const unsigned prong, const enum Transition::Type transition); 115 | inline void wideRequestRemain(); 116 | inline void wideRequestRestart(); 117 | inline void wideRequestResume(const unsigned prong); 118 | inline void wideChangeToRequested (const unsigned prong, Context& context, LoggerInterface* const logger); 119 | 120 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 121 | enum : unsigned { 122 | NameCount = Initial::NameCount, 123 | }; 124 | 125 | void wideGetNames(const unsigned parent, 126 | const unsigned depth, 127 | StateInfos& stateInfos) const; 128 | 129 | void wideIsActive(const unsigned prong, 130 | unsigned& index, 131 | MachineStructure& structure) const; 132 | #endif 133 | 134 | Initial initial; 135 | }; 136 | 137 | using SubStates = Sub<0, TS...>; 138 | 139 | //---------------------------------------------------------------------- 140 | 141 | enum : unsigned { 142 | ReverseDepth = SubStates::ReverseDepth + 1, 143 | DeepWidth = SubStates::DeepWidth, 144 | StateCount = State::StateCount + SubStates::StateCount, 145 | ForkCount = SubStates::ForkCount + 1, 146 | ProngCount = SubStates::ProngCount + sizeof...(TS), 147 | Width = sizeof...(TS), 148 | }; 149 | 150 | _C(StateRegistry& stateRegistry, 151 | const Parent parent, 152 | Parents& stateParents, 153 | Parents& forkParents, 154 | ForkPointers& forkPointers); 155 | 156 | inline void deepForwardSubstitute (Control& control, Context& context, LoggerInterface* const logger); 157 | inline void deepSubstitute (Control& control, Context& context, LoggerInterface* const logger); 158 | 159 | inline void deepEnterInitial ( Context& context, LoggerInterface* const logger); 160 | inline void deepEnter ( Context& context, LoggerInterface* const logger); 161 | 162 | inline bool deepUpdateAndTransition (Control& control, Context& context, LoggerInterface* const logger); 163 | inline void deepUpdate ( Context& context, LoggerInterface* const logger); 164 | 165 | template 166 | inline void deepReact (const TEvent& event, 167 | Control& control, Context& context, LoggerInterface* const logger); 168 | 169 | inline void deepLeave ( Context& context, LoggerInterface* const logger); 170 | 171 | inline void deepForwardRequest(const enum Transition::Type transition); 172 | inline void deepRequestRemain(); 173 | inline void deepRequestRestart(); 174 | inline void deepRequestResume(); 175 | inline void deepChangeToRequested ( Context& context, LoggerInterface* const logger); 176 | 177 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 178 | enum : unsigned { 179 | NameCount = State::NameCount + SubStates::NameCount, 180 | }; 181 | 182 | void deepGetNames(const unsigned parent, 183 | const enum StateInfo::RegionType region, 184 | const unsigned depth, 185 | StateInfos& stateInfos) const; 186 | 187 | void deepIsActive(const bool isActive, 188 | unsigned& index, 189 | MachineStructure& structure) const; 190 | #endif 191 | Fork _fork; 192 | State _state; 193 | SubStates _subStates; 194 | 195 | HSFM_IF_DEBUG(const TypeInfo _type = TypeInfo::get()); 196 | }; 197 | 198 | //////////////////////////////////////////////////////////////////////////////// 199 | 200 | } 201 | 202 | #include "machine_composite_methods.inl" 203 | #include "machine_composite_sub_1.inl" 204 | #include "machine_composite_sub_2.inl" 205 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_orthogonal_methods.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | M::_O::_O(StateRegistry& stateRegistry, 8 | const Parent parent, 9 | Parents& stateParents, 10 | Parents& forkParents, 11 | ForkPointers& forkPointers) 12 | : _fork(static_cast(forkPointers << &_fork), parent, forkParents) 13 | , _state(stateRegistry, parent, stateParents, forkParents, forkPointers) 14 | , _subStates(stateRegistry, _fork.self, stateParents, forkParents, forkPointers) 15 | {} 16 | 17 | //------------------------------------------------------------------------------ 18 | 19 | template 20 | template 21 | void 22 | M::_O::deepForwardSubstitute(Control& control, 23 | Context& context, 24 | LoggerInterface* const logger) 25 | { 26 | assert(_fork.active == INVALID_INDEX && 27 | _fork.resumable == INVALID_INDEX); 28 | 29 | if (_fork.requested != INVALID_INDEX) 30 | _subStates.wideForwardSubstitute(_fork.requested, control, context, logger); 31 | else 32 | _subStates.wideForwardSubstitute( control, context, logger); 33 | } 34 | 35 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 36 | 37 | template 38 | template 39 | void 40 | M::_O::deepSubstitute(Control& control, 41 | Context& context, 42 | LoggerInterface* const logger) 43 | { 44 | assert(_fork.active == INVALID_INDEX && 45 | _fork.resumable == INVALID_INDEX); 46 | 47 | if (!_state .deepSubstitute(control, context, logger)) 48 | _subStates.wideSubstitute(control, context, logger); 49 | } 50 | 51 | //------------------------------------------------------------------------------ 52 | 53 | template 54 | template 55 | void 56 | M::_O::deepEnterInitial(Context& context, 57 | LoggerInterface* const logger) 58 | { 59 | assert(_fork.active == INVALID_INDEX && 60 | _fork.resumable == INVALID_INDEX && 61 | _fork.requested == INVALID_INDEX); 62 | 63 | _state .deepEnter (context, logger); 64 | _subStates.wideEnterInitial(context, logger); 65 | } 66 | 67 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 68 | 69 | template 70 | template 71 | void 72 | M::_O::deepEnter(Context& context, 73 | LoggerInterface* const logger) 74 | { 75 | assert(_fork.active == INVALID_INDEX && 76 | _fork.resumable == INVALID_INDEX); 77 | 78 | _state .deepEnter(context, logger); 79 | _subStates.wideEnter(context, logger); 80 | } 81 | 82 | //------------------------------------------------------------------------------ 83 | 84 | template 85 | template 86 | bool 87 | M::_O::deepUpdateAndTransition(Control& control, 88 | Context& context, 89 | LoggerInterface* const logger) 90 | { 91 | assert(_fork.active == INVALID_INDEX && 92 | _fork.resumable == INVALID_INDEX); 93 | 94 | if (_state.deepUpdateAndTransition(control, context, logger)) { 95 | _subStates.wideUpdate(context, logger); 96 | 97 | return true; 98 | } else 99 | return _subStates.wideUpdateAndTransition(control, context, logger); 100 | } 101 | 102 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 103 | 104 | template 105 | template 106 | void 107 | M::_O::deepUpdate(Context& context, 108 | LoggerInterface* const logger) 109 | { 110 | assert(_fork.active == INVALID_INDEX && 111 | _fork.resumable == INVALID_INDEX); 112 | 113 | _state .deepUpdate(context, logger); 114 | _subStates.wideUpdate(context, logger); 115 | } 116 | 117 | //------------------------------------------------------------------------------ 118 | 119 | template 120 | template 121 | template 122 | void 123 | M::_O::deepReact(const TEvent& event, 124 | Control& control, 125 | Context& context, 126 | LoggerInterface* const logger) 127 | { 128 | assert(_fork.active == INVALID_INDEX && 129 | _fork.resumable == INVALID_INDEX); 130 | 131 | _state .deepReact(event, control, context, logger); 132 | _subStates.wideReact(event, control, context, logger); 133 | } 134 | 135 | //------------------------------------------------------------------------------ 136 | 137 | template 138 | template 139 | void 140 | M::_O::deepLeave(Context& context, 141 | LoggerInterface* const logger) 142 | { 143 | assert(_fork.active == INVALID_INDEX && 144 | _fork.resumable == INVALID_INDEX); 145 | 146 | _subStates.wideLeave(context, logger); 147 | _state .deepLeave(context, logger); 148 | } 149 | 150 | //------------------------------------------------------------------------------ 151 | 152 | template 153 | template 154 | void 155 | M::_O::deepForwardRequest(const enum Transition::Type transition) { 156 | assert(_fork.active == INVALID_INDEX && 157 | _fork.resumable == INVALID_INDEX); 158 | 159 | if (_fork.requested != INVALID_INDEX) 160 | _subStates.wideForwardRequest(_fork.requested, transition); 161 | else 162 | switch (transition) { 163 | case Transition::Remain: 164 | deepRequestRemain(); 165 | break; 166 | 167 | case Transition::Restart: 168 | deepRequestRestart(); 169 | break; 170 | 171 | case Transition::Resume: 172 | deepRequestResume(); 173 | break; 174 | 175 | default: 176 | assert(false); 177 | } 178 | } 179 | 180 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 181 | 182 | template 183 | template 184 | void 185 | M::_O::deepRequestRemain() { 186 | assert(_fork.active == INVALID_INDEX && 187 | _fork.resumable == INVALID_INDEX); 188 | 189 | _subStates.wideRequestRemain(); 190 | } 191 | 192 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 193 | 194 | template 195 | template 196 | void 197 | M::_O::deepRequestRestart() { 198 | assert(_fork.active == INVALID_INDEX && 199 | _fork.resumable == INVALID_INDEX); 200 | 201 | _subStates.wideRequestRestart(); 202 | } 203 | 204 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 205 | 206 | template 207 | template 208 | void 209 | M::_O::deepRequestResume() { 210 | assert(_fork.active == INVALID_INDEX && 211 | _fork.resumable == INVALID_INDEX); 212 | 213 | _subStates.wideRequestResume(); 214 | } 215 | 216 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 217 | 218 | template 219 | template 220 | void 221 | M::_O::deepChangeToRequested(Context& context, 222 | LoggerInterface* const logger) 223 | { 224 | assert(_fork.active == INVALID_INDEX && 225 | _fork.resumable == INVALID_INDEX); 226 | 227 | _subStates.wideChangeToRequested(context, logger); 228 | } 229 | 230 | //------------------------------------------------------------------------------ 231 | 232 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 233 | 234 | template 235 | template 236 | void 237 | M::_O::deepGetNames(const unsigned parent, 238 | const enum StateInfo::RegionType region, 239 | const unsigned depth, 240 | StateInfos& _stateInfos) const 241 | { 242 | _state.deepGetNames(parent, region, depth, _stateInfos); 243 | _subStates.wideGetNames(_stateInfos.count() - 1, depth + 1, _stateInfos); 244 | } 245 | 246 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 247 | 248 | template 249 | template 250 | void 251 | M::_O::deepIsActive(const bool isActive, 252 | unsigned& index, 253 | MachineStructure& structure) const 254 | { 255 | _state.deepIsActive(isActive, index, structure); 256 | _subStates.wideIsActive(isActive, index, structure); 257 | } 258 | 259 | #endif 260 | 261 | //////////////////////////////////////////////////////////////////////////////// 262 | 263 | } 264 | -------------------------------------------------------------------------------- /projects/visual-studio/HFSM.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 15.0 57 | {0895F78B-D3D4-4BEF-8E12-DAABE32686E5} 58 | Win32Proj 59 | 8.1 60 | 61 | 62 | 63 | Application 64 | true 65 | v140 66 | Unicode 67 | 68 | 69 | Application 70 | false 71 | v140 72 | true 73 | Unicode 74 | 75 | 76 | Application 77 | true 78 | v140 79 | Unicode 80 | 81 | 82 | Application 83 | false 84 | v140 85 | true 86 | Unicode 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | true 112 | 113 | 114 | false 115 | 116 | 117 | true 118 | 119 | 120 | false 121 | 122 | 123 | 124 | 125 | 126 | Disabled 127 | WIN32;_DEBUG;%(PreprocessorDefinitions) 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | MaxSpeed 136 | true 137 | true 138 | WIN32;NDEBUG;%(PreprocessorDefinitions) 139 | 140 | 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | Disabled 150 | _DEBUG;%(PreprocessorDefinitions) 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | MaxSpeed 159 | true 160 | true 161 | NDEBUG;%(PreprocessorDefinitions) 162 | 163 | 164 | true 165 | true 166 | 167 | 168 | 169 | 170 | 171 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_composite_sub_1.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | template 8 | M::_C::Sub::Sub(StateRegistry& stateRegistry, 9 | const Index fork, 10 | Parents& stateParents, 11 | Parents& forkParents, 12 | ForkPointers& forkPointers) 13 | : initial(stateRegistry, 14 | Parent(fork, 15 | ProngIndex 16 | HSFM_IF_DEBUG(, TypeInfo::get()) 17 | HSFM_IF_DEBUG(, TypeInfo::get())), 18 | stateParents, 19 | forkParents, 20 | forkPointers) 21 | , remaining(stateRegistry, fork, stateParents, forkParents, forkPointers) 22 | {} 23 | 24 | //------------------------------------------------------------------------------ 25 | 26 | template 27 | template 28 | template 29 | void 30 | M::_C::Sub::wideForwardSubstitute(const unsigned prong, 31 | Control& control, 32 | Context& context, 33 | LoggerInterface* const logger) 34 | { 35 | if (prong == ProngIndex) 36 | initial .deepForwardSubstitute( control, context, logger); 37 | else 38 | remaining.wideForwardSubstitute(prong, control, context, logger); 39 | } 40 | 41 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 42 | 43 | template 44 | template 45 | template 46 | void 47 | M::_C::Sub::wideSubstitute(const unsigned prong, 48 | Control& control, 49 | Context& context, 50 | LoggerInterface* const logger) 51 | { 52 | if (prong == ProngIndex) 53 | initial .deepSubstitute( control, context, logger); 54 | else 55 | remaining.wideSubstitute(prong, control, context, logger); 56 | } 57 | 58 | //------------------------------------------------------------------------------ 59 | 60 | template 61 | template 62 | template 63 | void 64 | M::_C::Sub::wideEnterInitial(Context& context, 65 | LoggerInterface* const logger) 66 | { 67 | initial.deepEnterInitial(context, logger); 68 | } 69 | 70 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 71 | 72 | template 73 | template 74 | template 75 | void 76 | M::_C::Sub::wideEnter(const unsigned prong, 77 | Context& context, 78 | LoggerInterface* const logger) 79 | { 80 | if (prong == ProngIndex) 81 | initial .deepEnter( context, logger); 82 | else 83 | remaining.wideEnter(prong, context, logger); 84 | } 85 | 86 | //------------------------------------------------------------------------------ 87 | 88 | template 89 | template 90 | template 91 | bool 92 | M::_C::Sub::wideUpdateAndTransition(const unsigned prong, 93 | Control& control, 94 | Context& context, 95 | LoggerInterface* const logger) 96 | { 97 | return prong == ProngIndex ? 98 | initial .deepUpdateAndTransition( control, context, logger) : 99 | remaining.wideUpdateAndTransition(prong, control, context, logger); 100 | } 101 | 102 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 103 | 104 | template 105 | template 106 | template 107 | void 108 | M::_C::Sub::wideUpdate(const unsigned prong, 109 | Context& context, 110 | LoggerInterface* const logger) 111 | { 112 | if (prong == ProngIndex) 113 | initial .deepUpdate( context, logger); 114 | else 115 | remaining.wideUpdate(prong, context, logger); 116 | } 117 | 118 | //------------------------------------------------------------------------------ 119 | 120 | template 121 | template 122 | template 123 | template 124 | void 125 | M::_C::Sub::wideReact(const unsigned prong, 126 | const TEvent& event, 127 | Control& control, 128 | Context& context, 129 | LoggerInterface* const logger) 130 | { 131 | if (prong == ProngIndex) 132 | initial .deepReact( event, control, context, logger); 133 | else 134 | remaining.wideReact(prong, event, control, context, logger); 135 | } 136 | 137 | //------------------------------------------------------------------------------ 138 | 139 | template 140 | template 141 | template 142 | void 143 | M::_C::Sub::wideLeave(const unsigned prong, 144 | Context& context, 145 | LoggerInterface* const logger) 146 | { 147 | if (prong == ProngIndex) 148 | initial .deepLeave( context, logger); 149 | else 150 | remaining.wideLeave(prong, context, logger); 151 | } 152 | 153 | //------------------------------------------------------------------------------ 154 | 155 | template 156 | template 157 | template 158 | void 159 | M::_C::Sub::wideForwardRequest(const unsigned prong, 160 | const enum Transition::Type transition) 161 | { 162 | if (prong == ProngIndex) 163 | initial .deepForwardRequest( transition); 164 | else 165 | remaining.wideForwardRequest(prong, transition); 166 | } 167 | 168 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 169 | 170 | template 171 | template 172 | template 173 | void 174 | M::_C::Sub::wideRequestRemain() { 175 | initial.deepRequestRemain(); 176 | } 177 | 178 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 179 | 180 | template 181 | template 182 | template 183 | void 184 | M::_C::Sub::wideRequestRestart() { 185 | initial.deepRequestRestart(); 186 | } 187 | 188 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 189 | 190 | template 191 | template 192 | template 193 | void 194 | M::_C::Sub::wideRequestResume(const unsigned prong) { 195 | if (prong == ProngIndex) 196 | initial.deepRequestResume(); 197 | else 198 | remaining.wideRequestResume(prong); 199 | } 200 | 201 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 202 | 203 | template 204 | template 205 | template 206 | void 207 | M::_C::Sub::wideChangeToRequested(const unsigned prong, 208 | Context& context, 209 | LoggerInterface* const logger) 210 | { 211 | if (prong == ProngIndex) 212 | initial .deepChangeToRequested( context, logger); 213 | else 214 | remaining.wideChangeToRequested(prong, context, logger); 215 | } 216 | 217 | //------------------------------------------------------------------------------ 218 | 219 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 220 | 221 | template 222 | template 223 | template 224 | void 225 | M::_C::Sub::wideGetNames(const unsigned parent, 226 | const unsigned depth, 227 | StateInfos& _stateInfos) const 228 | { 229 | initial.deepGetNames(parent, StateInfo::Composite, depth, _stateInfos); 230 | remaining.wideGetNames(parent, depth, _stateInfos); 231 | } 232 | 233 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 234 | 235 | template 236 | template 237 | template 238 | void 239 | M::_C::Sub::wideIsActive(const unsigned prong, 240 | unsigned& index, 241 | MachineStructure& structure) const 242 | { 243 | initial.deepIsActive(prong == ProngIndex, index, structure); 244 | remaining.wideIsActive(prong, index, structure); 245 | } 246 | 247 | #endif 248 | 249 | //////////////////////////////////////////////////////////////////////////////// 250 | 251 | } 252 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_orthogonal_sub_1.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | template 8 | M::_O::Sub::Sub(StateRegistry& stateRegistry, 9 | const Index fork, 10 | Parents& stateParents, 11 | Parents& forkParents, 12 | ForkPointers& forkPointers) 13 | : initial(stateRegistry, 14 | Parent(fork, 15 | ProngIndex 16 | HSFM_IF_DEBUG(, TypeInfo::get()) 17 | HSFM_IF_DEBUG(, TypeInfo::get())), 18 | stateParents, 19 | forkParents, 20 | forkPointers) 21 | , remaining(stateRegistry, fork, stateParents, forkParents, forkPointers) 22 | {} 23 | 24 | //------------------------------------------------------------------------------ 25 | 26 | template 27 | template 28 | template 29 | void 30 | M::_O::Sub::wideForwardSubstitute(const unsigned prong, 31 | Control& control, 32 | Context& context, 33 | LoggerInterface* const logger) 34 | { 35 | if (prong == ProngIndex) 36 | initial .deepForwardSubstitute( control, context, logger); 37 | else 38 | remaining.wideForwardSubstitute(prong, control, context, logger); 39 | } 40 | 41 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 42 | 43 | template 44 | template 45 | template 46 | void 47 | M::_O::Sub::wideForwardSubstitute(Control& control, 48 | Context& context, 49 | LoggerInterface* const logger) 50 | { 51 | initial .deepForwardSubstitute(control, context, logger); 52 | remaining.wideForwardSubstitute(control, context, logger); 53 | } 54 | 55 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 56 | 57 | template 58 | template 59 | template 60 | void 61 | M::_O::Sub::wideSubstitute(Control& control, 62 | Context& context, 63 | LoggerInterface* const logger) 64 | { 65 | initial .deepSubstitute(control, context, logger); 66 | remaining.wideSubstitute(control, context, logger); 67 | } 68 | 69 | //------------------------------------------------------------------------------ 70 | 71 | template 72 | template 73 | template 74 | void 75 | M::_O::Sub::wideEnterInitial(Context& context, 76 | LoggerInterface* const logger) 77 | { 78 | initial .deepEnterInitial(context, logger); 79 | remaining.wideEnterInitial(context, logger); 80 | } 81 | 82 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 83 | 84 | template 85 | template 86 | template 87 | void 88 | M::_O::Sub::wideEnter(Context& context, 89 | LoggerInterface* const logger) 90 | { 91 | initial .deepEnter(context, logger); 92 | remaining.wideEnter(context, logger); 93 | } 94 | 95 | //------------------------------------------------------------------------------ 96 | 97 | template 98 | template 99 | template 100 | bool 101 | M::_O::Sub::wideUpdateAndTransition(Control& control, 102 | Context& context, 103 | LoggerInterface* const logger) 104 | { 105 | return initial .deepUpdateAndTransition(control, context, logger) 106 | || remaining.wideUpdateAndTransition(control, context, logger); 107 | } 108 | 109 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 110 | 111 | template 112 | template 113 | template 114 | void 115 | M::_O::Sub::wideUpdate(Context& context, 116 | LoggerInterface* const logger) 117 | { 118 | initial .deepUpdate(context, logger); 119 | remaining.wideUpdate(context, logger); 120 | } 121 | 122 | //------------------------------------------------------------------------------ 123 | 124 | template 125 | template 126 | template 127 | template 128 | void 129 | M::_O::Sub::wideReact(const TEvent& event, 130 | Control& control, 131 | Context& context, 132 | LoggerInterface* const logger) 133 | { 134 | initial .deepReact(event, control, context, logger); 135 | remaining.wideReact(event, control, context, logger); 136 | } 137 | 138 | //------------------------------------------------------------------------------ 139 | 140 | template 141 | template 142 | template 143 | void 144 | M::_O::Sub::wideLeave(Context& context, 145 | LoggerInterface* const logger) 146 | { 147 | initial .deepLeave(context, logger); 148 | remaining.wideLeave(context, logger); 149 | } 150 | 151 | //------------------------------------------------------------------------------ 152 | 153 | template 154 | template 155 | template 156 | void 157 | M::_O::Sub::wideForwardRequest(const unsigned prong, 158 | const enum Transition::Type transition) 159 | { 160 | if (prong == ProngIndex) { 161 | initial.deepForwardRequest(transition); 162 | remaining.wideForwardRequest(prong, Transition::Remain); 163 | } else { 164 | initial.deepForwardRequest(Transition::Remain); 165 | remaining.wideForwardRequest(prong, transition); 166 | } 167 | } 168 | 169 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 170 | 171 | template 172 | template 173 | template 174 | void 175 | M::_O::Sub::wideRequestRemain() { 176 | initial.deepRequestRemain(); 177 | remaining.wideRequestRemain(); 178 | } 179 | 180 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 181 | 182 | template 183 | template 184 | template 185 | void 186 | M::_O::Sub::wideRequestRestart() { 187 | initial.deepRequestRestart(); 188 | remaining.wideRequestRestart(); 189 | } 190 | 191 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 192 | 193 | template 194 | template 195 | template 196 | void 197 | M::_O::Sub::wideRequestResume() { 198 | initial.deepRequestResume(); 199 | remaining.wideRequestResume(); 200 | } 201 | 202 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 203 | 204 | template 205 | template 206 | template 207 | void 208 | M::_O::Sub::wideChangeToRequested(Context& context, 209 | LoggerInterface* const logger) 210 | { 211 | initial .deepChangeToRequested(context, logger); 212 | remaining.wideChangeToRequested(context, logger); 213 | } 214 | 215 | //------------------------------------------------------------------------------ 216 | 217 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 218 | 219 | template 220 | template 221 | template 222 | void 223 | M::_O::Sub::wideGetNames(const unsigned parent, 224 | const unsigned depth, 225 | StateInfos& _stateInfos) const 226 | { 227 | initial.deepGetNames(parent, StateInfo::Orthogonal, depth, _stateInfos); 228 | remaining.wideGetNames(parent, depth, _stateInfos); 229 | } 230 | 231 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 232 | 233 | template 234 | template 235 | template 236 | void 237 | M::_O::Sub::wideIsActive(const bool isActive, 238 | unsigned& index, 239 | MachineStructure& structure) const 240 | { 241 | initial.deepIsActive(isActive, index, structure); 242 | remaining.wideIsActive(isActive, index, structure); 243 | } 244 | 245 | #endif 246 | 247 | //////////////////////////////////////////////////////////////////////////////// 248 | 249 | } 250 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine_composite_methods.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | template 6 | template 7 | M::_C::_C(StateRegistry& stateRegistry, 8 | const Parent parent, 9 | Parents& stateParents, 10 | Parents& forkParents, 11 | ForkPointers& forkPointers) 12 | : _fork(static_cast(forkPointers << &_fork), parent, forkParents) 13 | , _state(stateRegistry, parent, stateParents, forkParents, forkPointers) 14 | , _subStates(stateRegistry, _fork.self, stateParents, forkParents, forkPointers) 15 | {} 16 | 17 | //------------------------------------------------------------------------------ 18 | 19 | template 20 | template 21 | void 22 | M::_C::deepForwardSubstitute(Control& control, 23 | Context& context, 24 | LoggerInterface* const logger) 25 | { 26 | assert(_fork.requested != INVALID_INDEX); 27 | 28 | if (_fork.requested == _fork.active) 29 | _subStates.wideForwardSubstitute(_fork.requested, control, context, logger); 30 | else 31 | _subStates.wideSubstitute (_fork.requested, control, context, logger); 32 | } 33 | 34 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 35 | 36 | template 37 | template 38 | void 39 | M::_C::deepSubstitute(Control& control, 40 | Context& context, 41 | LoggerInterface* const logger) 42 | { 43 | assert(_fork.active == INVALID_INDEX && 44 | _fork.requested != INVALID_INDEX); 45 | 46 | if (!_state .deepSubstitute( control, context, logger)) 47 | _subStates.wideSubstitute(_fork.requested, control, context, logger); 48 | } 49 | 50 | //------------------------------------------------------------------------------ 51 | 52 | template 53 | template 54 | void 55 | M::_C::deepEnterInitial(Context& context, 56 | LoggerInterface* const logger) 57 | { 58 | assert(_fork.active == INVALID_INDEX && 59 | _fork.resumable == INVALID_INDEX && 60 | _fork.requested == INVALID_INDEX); 61 | 62 | HSFM_IF_DEBUG(_fork.activeType = TypeInfo::get()); 63 | _fork.active = 0; 64 | 65 | _state .deepEnter (context, logger); 66 | _subStates.wideEnterInitial(context, logger); 67 | } 68 | 69 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 70 | 71 | template 72 | template 73 | void 74 | M::_C::deepEnter(Context& context, 75 | LoggerInterface* const logger) 76 | { 77 | assert(_fork.active == INVALID_INDEX && 78 | _fork.requested != INVALID_INDEX); 79 | 80 | HSFM_IF_DEBUG(_fork.activeType = _fork.requestedType); 81 | _fork.active = _fork.requested; 82 | 83 | HSFM_IF_DEBUG(_fork.requestedType.clear()); 84 | _fork.requested = INVALID_INDEX; 85 | 86 | _state .deepEnter( context, logger); 87 | _subStates.wideEnter(_fork.active, context, logger); 88 | } 89 | 90 | //------------------------------------------------------------------------------ 91 | 92 | template 93 | template 94 | bool 95 | M::_C::deepUpdateAndTransition(Control& control, 96 | Context& context, 97 | LoggerInterface* const logger) 98 | { 99 | assert(_fork.active != INVALID_INDEX); 100 | 101 | if (_state.deepUpdateAndTransition(control, context, logger)) { 102 | _subStates.wideUpdate(_fork.active, context, logger); 103 | 104 | return true; 105 | } else 106 | return _subStates.wideUpdateAndTransition(_fork.active, control, context, logger); 107 | } 108 | 109 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 110 | 111 | template 112 | template 113 | void 114 | M::_C::deepUpdate(Context& context, 115 | LoggerInterface* const logger) 116 | { 117 | assert(_fork.active != INVALID_INDEX); 118 | 119 | _state .deepUpdate( context, logger); 120 | _subStates.wideUpdate(_fork.active, context, logger); 121 | } 122 | 123 | //------------------------------------------------------------------------------ 124 | 125 | template 126 | template 127 | template 128 | void 129 | M::_C::deepReact(const TEvent& event, 130 | Control& control, 131 | Context& context, 132 | LoggerInterface* const logger) 133 | { 134 | assert(_fork.active != INVALID_INDEX); 135 | 136 | _state .deepReact( event, control, context, logger); 137 | _subStates.wideReact(_fork.active, event, control, context, logger); 138 | } 139 | 140 | //------------------------------------------------------------------------------ 141 | 142 | template 143 | template 144 | void 145 | M::_C::deepLeave(Context& context, 146 | LoggerInterface* const logger) 147 | { 148 | assert(_fork.active != INVALID_INDEX); 149 | 150 | _subStates.wideLeave(_fork.active, context, logger); 151 | _state .deepLeave( context, logger); 152 | 153 | HSFM_IF_DEBUG(_fork.resumableType = _fork.activeType); 154 | _fork.resumable = _fork.active; 155 | 156 | HSFM_IF_DEBUG(_fork.activeType.clear()); 157 | _fork.active = INVALID_INDEX; 158 | } 159 | 160 | //------------------------------------------------------------------------------ 161 | 162 | template 163 | template 164 | void 165 | M::_C::deepForwardRequest(const enum Transition::Type transition) { 166 | if (_fork.requested != INVALID_INDEX) 167 | _subStates.wideForwardRequest(_fork.requested, transition); 168 | else 169 | switch (transition) { 170 | case Transition::Remain: 171 | deepRequestRemain(); 172 | break; 173 | 174 | case Transition::Restart: 175 | deepRequestRestart(); 176 | break; 177 | 178 | case Transition::Resume: 179 | deepRequestResume(); 180 | break; 181 | 182 | default: 183 | assert(false); 184 | } 185 | } 186 | 187 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 188 | 189 | template 190 | template 191 | void 192 | M::_C::deepRequestRemain() { 193 | if (_fork.active == INVALID_INDEX) { 194 | HSFM_IF_DEBUG(_fork.requestedType = TypeInfo::get()); 195 | _fork.requested = 0; 196 | } 197 | 198 | _subStates.wideRequestRemain(); 199 | } 200 | 201 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 202 | 203 | template 204 | template 205 | void 206 | M::_C::deepRequestRestart() { 207 | HSFM_IF_DEBUG(_fork.requestedType = TypeInfo::get()); 208 | _fork.requested = 0; 209 | 210 | _subStates.wideRequestRestart(); 211 | } 212 | 213 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 214 | 215 | template 216 | template 217 | void 218 | M::_C::deepRequestResume() { 219 | if (_fork.resumable != INVALID_INDEX) { 220 | HSFM_IF_DEBUG(_fork.requestedType = _fork.resumableType); 221 | _fork.requested = _fork.resumable; 222 | } else { 223 | HSFM_IF_DEBUG(_fork.requestedType = TypeInfo::get()); 224 | _fork.requested = 0; 225 | } 226 | 227 | _subStates.wideRequestResume(_fork.requested); 228 | } 229 | 230 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 231 | 232 | template 233 | template 234 | void 235 | M::_C::deepChangeToRequested(Context& context, 236 | LoggerInterface* const logger) 237 | { 238 | assert(_fork.active != INVALID_INDEX); 239 | 240 | if (_fork.requested == _fork.active) 241 | _subStates.wideChangeToRequested(_fork.requested, context, logger); 242 | else if (_fork.requested != INVALID_INDEX) { 243 | _subStates.wideLeave(_fork.active, context, logger); 244 | 245 | HSFM_IF_DEBUG(_fork.resumableType = _fork.activeType); 246 | _fork.resumable = _fork.active; 247 | 248 | HSFM_IF_DEBUG(_fork.activeType = _fork.requestedType); 249 | _fork.active = _fork.requested; 250 | 251 | HSFM_IF_DEBUG(_fork.requestedType.clear()); 252 | _fork.requested = INVALID_INDEX; 253 | 254 | _subStates.wideEnter(_fork.active, context, logger); 255 | } 256 | } 257 | 258 | //------------------------------------------------------------------------------ 259 | 260 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 261 | 262 | template 263 | template 264 | void 265 | M::_C::deepGetNames(const unsigned parent, 266 | const enum StateInfo::RegionType region, 267 | const unsigned depth, 268 | StateInfos& _stateInfos) const 269 | { 270 | _state.deepGetNames(parent, region, depth, _stateInfos); 271 | _subStates.wideGetNames(_stateInfos.count() - 1, depth + 1, _stateInfos); 272 | } 273 | 274 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 275 | 276 | template 277 | template 278 | void 279 | M::_C::deepIsActive(const bool isActive, 280 | unsigned& index, 281 | MachineStructure& structure) const 282 | { 283 | _state.deepIsActive(isActive, index, structure); 284 | _subStates.wideIsActive(isActive ? _fork.active : INVALID_INDEX, index, structure); 285 | } 286 | 287 | #endif 288 | 289 | //////////////////////////////////////////////////////////////////////////////// 290 | 291 | } 292 | -------------------------------------------------------------------------------- /include/hfsm/detail/machine.inl: -------------------------------------------------------------------------------- 1 | namespace hfsm { 2 | 3 | //////////////////////////////////////////////////////////////////////////////// 4 | 5 | #pragma region Utility 6 | 7 | template 8 | M::Fork::Fork(const Index index, 9 | const TypeInfo HSFM_IF_DEBUG(type_)) 10 | : self(index) 11 | HSFM_IF_DEBUG(, type(type_)) 12 | {} 13 | 14 | //------------------------------------------------------------------------------ 15 | 16 | template 17 | template 18 | unsigned 19 | M::StateRegistryT::add(const TypeInfo stateType) { 20 | const unsigned index = _typeToIndex.count(); 21 | 22 | HSFM_CHECKED(_typeToIndex.insert(*stateType, index)); 23 | 24 | return index; 25 | } 26 | 27 | #pragma endregion 28 | 29 | //////////////////////////////////////////////////////////////////////////////// 30 | 31 | #pragma region Injections 32 | 33 | template 34 | template 35 | void 36 | M::_B::widePreSubstitute(Context& context) { 37 | TI::preSubstitute(context); 38 | _B::widePreSubstitute(context); 39 | } 40 | 41 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 42 | 43 | template 44 | template 45 | void 46 | M::_B::widePreEnter(Context& context) { 47 | TI::preEnter(context); 48 | _B::widePreEnter(context); 49 | } 50 | 51 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 52 | 53 | template 54 | template 55 | void 56 | M::_B::widePreUpdate(Context& context) { 57 | TI::preUpdate(context); 58 | _B::widePreUpdate(context); 59 | } 60 | 61 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 62 | 63 | template 64 | template 65 | void 66 | M::_B::widePreTransition(Context& context) { 67 | TI::preTransition(context); 68 | _B::widePreTransition(context); 69 | } 70 | 71 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 72 | 73 | template 74 | template 75 | template 76 | void 77 | M::_B::widePreReact(const TEvent& event, 78 | Context& context) 79 | { 80 | TI::preReact(event, context); 81 | _B::widePreReact(event, context); 82 | } 83 | 84 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 85 | 86 | template 87 | template 88 | void 89 | M::_B::widePostLeave(Context& context) { 90 | TI::postLeave(context); 91 | _B::widePostLeave(context); 92 | } 93 | 94 | //------------------------------------------------------------------------------ 95 | 96 | template 97 | template 98 | void 99 | M::_B::widePreSubstitute(Context& context) { 100 | TI::preSubstitute(context); 101 | } 102 | 103 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 104 | 105 | template 106 | template 107 | void 108 | M::_B::widePreEnter(Context& context) { 109 | TI::preEnter(context); 110 | } 111 | 112 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 113 | 114 | template 115 | template 116 | void 117 | M::_B::widePreUpdate(Context& context) { 118 | TI::preUpdate(context); 119 | } 120 | 121 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 122 | 123 | template 124 | template 125 | void 126 | M::_B::widePreTransition(Context& context) { 127 | TI::preTransition(context); 128 | } 129 | 130 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 131 | 132 | template 133 | template 134 | template 135 | void 136 | M::_B::widePreReact(const TEvent& event, 137 | Context& context) 138 | { 139 | TI::preReact(event, context); 140 | } 141 | 142 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 143 | 144 | template 145 | template 146 | void 147 | M::_B::widePostLeave(Context& context) { 148 | TI::postLeave(context); 149 | } 150 | 151 | #pragma endregion 152 | 153 | //////////////////////////////////////////////////////////////////////////////// 154 | 155 | #pragma region Root 156 | 157 | template 158 | template 159 | M::_R::_R(Context& context 160 | HFSM_IF_LOGGER(, LoggerInterface* const logger)) 161 | : _context(context) 162 | , _apex(_stateRegistry, Parent(), _stateParents, _forkParents, _forkPointers) 163 | HFSM_IF_LOGGER(, _logger(logger)) 164 | { 165 | HFSM_IF_STRUCTURE(getStateNames()); 166 | 167 | _apex.deepEnterInitial(_context, HFSM_LOGGER_OR(_logger, nullptr)); 168 | 169 | HFSM_IF_STRUCTURE(udpateActivity()); 170 | } 171 | 172 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 173 | 174 | template 175 | template 176 | M::_R::~_R() { 177 | _apex.deepLeave(_context, HFSM_LOGGER_OR(_logger, nullptr)); 178 | } 179 | 180 | //------------------------------------------------------------------------------ 181 | 182 | template 183 | template 184 | void 185 | M::_R::update() { 186 | Control control(_requests); 187 | _apex.deepUpdateAndTransition(control, _context, HFSM_LOGGER_OR(_logger, nullptr)); 188 | 189 | if (_requests.count()) 190 | processTransitions(); 191 | } 192 | 193 | //------------------------------------------------------------------------------ 194 | 195 | template 196 | template 197 | template 198 | void 199 | M::_R::react(const TEvent& event) { 200 | Control control(_requests); 201 | _apex.deepReact(event, control, _context, HFSM_LOGGER_OR(_logger, nullptr)); 202 | 203 | if (_requests.count()) 204 | processTransitions(); 205 | } 206 | 207 | //------------------------------------------------------------------------------ 208 | 209 | template 210 | template 211 | template 212 | bool 213 | M::_R::isActive() { 214 | using Type = T; 215 | 216 | const auto stateType = TypeInfo::get(); 217 | const auto state = _stateRegistry[*stateType]; 218 | 219 | for (auto parent = _stateParents[state]; parent; parent = _forkParents[parent.fork]) { 220 | const auto& fork = *_forkPointers[parent.fork]; 221 | 222 | if (fork.active != INVALID_INDEX) 223 | return parent.prong == fork.active; 224 | } 225 | 226 | return false; 227 | } 228 | 229 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 230 | 231 | template 232 | template 233 | template 234 | bool 235 | M::_R::isResumable() { 236 | using Type = T; 237 | 238 | const auto stateType = TypeInfo::get(); 239 | const auto state = _stateRegistry[*stateType]; 240 | 241 | for (auto parent = _stateParents[state]; parent; parent = _forkParents[parent.fork]) { 242 | const auto& fork = *_forkPointers[parent.fork]; 243 | 244 | if (fork.active != INVALID_INDEX) 245 | return parent.prong == fork.resumable; 246 | } 247 | 248 | return false; 249 | } 250 | 251 | //------------------------------------------------------------------------------ 252 | 253 | template 254 | template 255 | void 256 | M::_R::processTransitions() { 257 | HFSM_IF_STRUCTURE(_lastTransitions.clear()); 258 | 259 | for (unsigned i = 0; 260 | i < MaxSubstitutions && _requests.count(); 261 | ++i) 262 | { 263 | unsigned changeCount = 0; 264 | 265 | for (const auto& request : _requests) { 266 | HFSM_IF_STRUCTURE(_lastTransitions << DebugTransitionInfo(request, DebugTransitionInfo::Update)); 267 | 268 | switch (request.type) { 269 | case Transition::Restart: 270 | case Transition::Resume: 271 | requestImmediate(request); 272 | 273 | ++changeCount; 274 | break; 275 | 276 | case Transition::Schedule: 277 | requestScheduled(request); 278 | break; 279 | 280 | default: 281 | assert(false); 282 | } 283 | } 284 | _requests.clear(); 285 | 286 | if (changeCount > 0) { 287 | Control substitutionControl(_requests); 288 | _apex.deepForwardSubstitute(substitutionControl, _context, HFSM_LOGGER_OR(_logger, nullptr)); 289 | 290 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 291 | for (const auto& request : _requests) 292 | _lastTransitions << DebugTransitionInfo(request, DebugTransitionInfo::Substitute); 293 | #endif 294 | } 295 | } 296 | 297 | _apex.deepChangeToRequested(_context, HFSM_LOGGER_OR(_logger, nullptr)); 298 | 299 | HFSM_IF_STRUCTURE(udpateActivity()); 300 | } 301 | 302 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 303 | 304 | template 305 | template 306 | void 307 | M::_R::requestImmediate(const Transition request) { 308 | const unsigned state = id(request); 309 | 310 | for (auto parent = _stateParents[state]; parent; parent = _forkParents[parent.fork]) { 311 | auto& fork = *_forkPointers[parent.fork]; 312 | 313 | HSFM_IF_DEBUG(fork.requestedType = parent.prongType); 314 | fork.requested = parent.prong; 315 | } 316 | 317 | _apex.deepForwardRequest(request.type); 318 | } 319 | 320 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 321 | 322 | template 323 | template 324 | void 325 | M::_R::requestScheduled(const Transition request) { 326 | const unsigned state = id(request); 327 | 328 | const auto parent = _stateParents[state]; 329 | auto& fork = *_forkPointers[parent.fork]; 330 | 331 | HSFM_IF_ASSERT(const auto forksParent = _stateParents[fork.self]); 332 | HSFM_IF_ASSERT(const auto& forksFork = *_forkPointers[forksParent.fork]); 333 | assert(forksFork.active == INVALID_INDEX); 334 | 335 | HSFM_IF_DEBUG(fork.resumableType = parent.prongType); 336 | fork.resumable = parent.prong; 337 | } 338 | 339 | //------------------------------------------------------------------------------ 340 | 341 | #ifdef HFSM_ENABLE_STRUCTURE_REPORT 342 | 343 | template 344 | template 345 | void 346 | M::_R::getStateNames() { 347 | _stateInfos.clear(); 348 | _apex.deepGetNames((unsigned) -1, StateInfo::Composite, 0, _stateInfos); 349 | 350 | unsigned margin = (unsigned) -1; 351 | for (unsigned s = 0; s < _stateInfos.count(); ++s) { 352 | const auto& state = _stateInfos[s]; 353 | auto& prefix = _prefixes[s]; 354 | 355 | if (margin > state.depth && state.name[0] != '\0') 356 | margin = state.depth; 357 | 358 | if (state.depth == 0) 359 | prefix[0] = L'\0'; 360 | else { 361 | const auto mark = state.depth * 2 - 1; 362 | 363 | prefix[mark + 0] = state.region == StateInfo::Composite ? L'└' : L'╙'; 364 | prefix[mark + 1] = L' '; 365 | prefix[mark + 2] = L'\0'; 366 | 367 | for (unsigned d = mark; d > 0; --d) 368 | prefix[d - 1] = L' '; 369 | 370 | for (unsigned r = s; r > state.parent; --r) { 371 | auto& prefixAbove = _prefixes[r - 1]; 372 | 373 | switch (prefixAbove[mark]) { 374 | case L' ': 375 | prefixAbove[mark] = state.region == StateInfo::Composite ? L'│' : L'║'; 376 | break; 377 | case L'└': 378 | prefixAbove[mark] = L'├'; 379 | break; 380 | case L'╙': 381 | prefixAbove[mark] = L'╟'; 382 | break; 383 | } 384 | } 385 | } 386 | } 387 | if (margin > 0) 388 | margin -= 1; 389 | 390 | _structure.clear(); 391 | for (unsigned s = 0; s < _stateInfos.count(); ++s) { 392 | const auto& state = _stateInfos[s]; 393 | auto& prefix = _prefixes[s]; 394 | const auto space = state.depth * 2; 395 | 396 | if (state.name[0] != L'\0') { 397 | _structure << StructureEntry { false, &prefix[margin * 2], state.name }; 398 | _activityHistory << (char) 0; 399 | } else if (s + 1 < _stateInfos.count()) { 400 | auto& nextPrefix = _prefixes[s + 1]; 401 | 402 | if (s > 0) 403 | for (unsigned c = 0; c <= space; ++c) 404 | nextPrefix[c] = prefix[c]; 405 | 406 | const auto mark = space + 1; 407 | 408 | switch (nextPrefix[mark]) { 409 | case L'├': 410 | nextPrefix[mark] = state.depth == margin ? L'┌' : L'┬'; 411 | break; 412 | case L'╟': 413 | nextPrefix[mark] = state.depth == margin ? L'╓' : L'╥'; 414 | break; 415 | } 416 | } 417 | } 418 | } 419 | 420 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 421 | 422 | template 423 | template 424 | void 425 | M::_R::udpateActivity() { 426 | for (auto& item : _structure) 427 | item.isActive = false; 428 | 429 | unsigned index = 0; 430 | _apex.deepIsActive(true, index, _structure); 431 | 432 | for (unsigned i = 0; i < _structure.count(); ++i) { 433 | auto& activity = _activityHistory[i]; 434 | 435 | if (_structure[i].isActive) { 436 | if (activity > 0) 437 | activity = activity < std::numeric_limits::max() ? activity + 1 : activity; 438 | else 439 | activity = +1; 440 | } else { 441 | if (activity > 0) 442 | activity = -1; 443 | else 444 | activity = activity > std::numeric_limits::min() ? activity - 1 : activity; 445 | } 446 | } 447 | } 448 | 449 | #endif 450 | 451 | #pragma endregion 452 | 453 | //////////////////////////////////////////////////////////////////////////////// 454 | 455 | } 456 | 457 | #include "machine_state.hpp" 458 | #include "machine_composite.hpp" 459 | #include "machine_orthogonal.hpp" 460 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define HFSM_ENABLE_STRUCTURE_REPORT 2 | //#include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | 10 | namespace Event { 11 | enum Enum : unsigned { 12 | Substitute, 13 | Enter, 14 | Update, 15 | Transition, 16 | ReactionRequest, 17 | Reaction, 18 | Leave, 19 | 20 | Restart, 21 | Resume, 22 | Schedule, 23 | 24 | COUNT 25 | }; 26 | }; 27 | 28 | //------------------------------------------------------------------------------ 29 | 30 | struct Status { 31 | Event::Enum func; 32 | hfsm::detail::TypeInfo state; 33 | 34 | inline bool operator == (const Status& reference) const { 35 | return func == reference.func && state == reference.state; 36 | } 37 | }; 38 | 39 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 40 | 41 | template 42 | Status status(Event::Enum event) { 43 | using Type = T; 44 | 45 | return Status{ event, std::type_index(typeid(Type)) }; 46 | } 47 | 48 | //------------------------------------------------------------------------------ 49 | 50 | struct Context { 51 | using History = std::vector; 52 | 53 | template 54 | void assertHistory(const Status (&reference)[TCapacity]) { 55 | const unsigned historySize = (unsigned)history.size(); 56 | const unsigned referenceSize = hfsm::detail::count(reference); 57 | assert(historySize == referenceSize); 58 | 59 | for (unsigned i = 0; i < std::min(historySize, referenceSize); ++i) { 60 | HSFM_IF_ASSERT(const auto h = history[i]); 61 | HSFM_IF_ASSERT(const auto r = reference[i]); 62 | assert(h == r); 63 | } 64 | 65 | history.clear(); 66 | } 67 | 68 | float deltaTime = 0.0f; 69 | 70 | History history; 71 | }; 72 | using M = hfsm::Machine; 73 | 74 | //------------------------------------------------------------------------------ 75 | 76 | class Timed 77 | : public M::Bare 78 | { 79 | public: 80 | void preEnter(Context&) { _elapsed = 0.0f; } 81 | void preUpdate(Context& _) { _elapsed += _.deltaTime; } 82 | 83 | auto elapsed() const { return _elapsed; } 84 | 85 | private: 86 | float _elapsed; 87 | }; 88 | 89 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 90 | 91 | class Tracked 92 | : public M::Bare 93 | { 94 | public: 95 | void preEnter(Context&) { 96 | ++_entryCount; 97 | _currentUpdateCount = 0; 98 | } 99 | 100 | void preUpdate(Context&) { 101 | ++_currentUpdateCount; 102 | ++_totalUpdateCount; 103 | } 104 | 105 | unsigned entryCount() const { return _entryCount; } 106 | unsigned currentUpdateCount() const { return _currentUpdateCount; } 107 | unsigned totalUpdateCount() const { return _totalUpdateCount; } 108 | 109 | private: 110 | unsigned _entryCount = 0; 111 | unsigned _currentUpdateCount = 0; 112 | unsigned _totalUpdateCount = 0; 113 | }; 114 | 115 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 116 | 117 | struct Action {}; 118 | 119 | template 120 | struct HistoryBase 121 | : M::Bare 122 | { 123 | void preSubstitute(Context& _) const { 124 | _.history.push_back(Status{ Event::Substitute, hfsm::detail::TypeInfo::get() }); 125 | } 126 | 127 | void preEnter(Context& _) { 128 | _.history.push_back(Status{ Event::Enter, hfsm::detail::TypeInfo::get() }); 129 | } 130 | 131 | void preUpdate(Context& _) { 132 | _.history.push_back(Status{ Event::Update, hfsm::detail::TypeInfo::get() }); 133 | } 134 | 135 | void preTransition(Context& _) const { 136 | _.history.push_back(Status{ Event::Transition, hfsm::detail::TypeInfo::get() }); 137 | } 138 | 139 | void preReact(const Action&, Context& _) { 140 | _.history.push_back(Status{ Event::ReactionRequest, hfsm::detail::TypeInfo::get() }); 141 | } 142 | 143 | void postLeave(Context& _) { 144 | _.history.push_back(Status{ Event::Leave, hfsm::detail::TypeInfo::get() }); 145 | } 146 | }; 147 | 148 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 149 | 150 | template 151 | using Base = M::BaseT>; 152 | 153 | //------------------------------------------------------------------------------ 154 | 155 | template 156 | void 157 | changeTo(M::Control& control, Context::History& history) { 158 | control.template changeTo(); 159 | history.push_back(Status{ Event::Restart, hfsm::detail::TypeInfo::get() }); 160 | } 161 | 162 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 163 | 164 | template 165 | void 166 | resume(M::Control& control, Context::History& history) { 167 | control.template resume(); 168 | history.push_back(Status{ Event::Resume, hfsm::detail::TypeInfo::get() }); 169 | } 170 | 171 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 172 | 173 | template 174 | void 175 | schedule(M::Control& control, Context::History& history) { 176 | control.template schedule(); 177 | history.push_back(Status{ Event::Schedule, hfsm::detail::TypeInfo::get() }); 178 | } 179 | 180 | //////////////////////////////////////////////////////////////////////////////// 181 | 182 | template 183 | struct Reacting 184 | : Base 185 | { 186 | void react(const Action&, M::Control&, Context& _) { 187 | _.history.push_back(Status{ Event::Reaction, hfsm::detail::TypeInfo::get() }); 188 | } 189 | }; 190 | 191 | //////////////////////////////////////////////////////////////////////////////// 192 | 193 | struct A : Reacting {}; 194 | 195 | //------------------------------------------------------------------------------ 196 | 197 | struct A_2; 198 | 199 | struct A_1 200 | : Base 201 | { 202 | void transition(Control& control, Context& _) { 203 | changeTo(control, _.history); 204 | } 205 | }; 206 | 207 | //------------------------------------------------------------------------------ 208 | 209 | struct B; 210 | struct B_2_2; 211 | 212 | struct A_2 213 | : Base 214 | { 215 | void transition(Control& control, Context& _) { 216 | switch (entryCount()) { 217 | case 1: 218 | changeTo(control, _.history); 219 | break; 220 | 221 | case 2: 222 | resume(control, _.history); 223 | break; 224 | } 225 | } 226 | }; 227 | 228 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 229 | 230 | struct A_2_1 : Reacting {}; 231 | struct A_2_2 : Reacting {}; 232 | 233 | //------------------------------------------------------------------------------ 234 | 235 | struct B : Reacting {}; 236 | 237 | struct B_1 : Base {}; 238 | struct B_1_1 : Base {}; 239 | struct B_1_2 : Base {}; 240 | 241 | struct B_2 : Base {}; 242 | 243 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 244 | 245 | struct B_2_1 246 | : Base 247 | { 248 | void substitute(Control& control, Context& _) { 249 | resume(control, _.history); 250 | } 251 | }; 252 | 253 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 254 | 255 | struct B_2_2 256 | : Base 257 | { 258 | void substitute(Control& control, Context&) { 259 | if (entryCount() == 2) 260 | control.resume(); 261 | } 262 | 263 | void transition(Control& control, Context& _) { 264 | switch (totalUpdateCount()) { 265 | case 1: 266 | resume(control, _.history); 267 | break; 268 | 269 | case 2: 270 | changeTo(control, _.history); 271 | break; 272 | 273 | case 3: 274 | schedule(control, _.history); 275 | resume(control, _.history); 276 | break; 277 | } 278 | } 279 | }; 280 | 281 | //////////////////////////////////////////////////////////////////////////////// 282 | 283 | int 284 | main(int, char*[]) { 285 | Context _; 286 | 287 | { 288 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 289 | 290 | M::PeerRoot< 291 | M::Composite 297 | >, 298 | M::Orthogonal, 303 | M::Composite 307 | > 308 | > machine(_); 309 | 310 | static_assert(machine.DeepWidth == 2, ""); 311 | static_assert(machine.StateCount == 13, ""); 312 | static_assert(machine.ForkCount == 6, ""); 313 | static_assert(machine.ProngCount == 10, ""); 314 | 315 | const Status created[] = { 316 | status(Event::Enter), 317 | status(Event::Enter), 318 | }; 319 | _.assertHistory(created); 320 | 321 | assert( machine.isActive()); 322 | assert( machine.isActive()); 323 | assert(!machine.isActive()); 324 | assert(!machine.isActive()); 325 | assert(!machine.isActive()); 326 | assert(!machine.isActive()); 327 | assert(!machine.isActive()); 328 | assert(!machine.isActive()); 329 | assert(!machine.isActive()); 330 | assert(!machine.isActive()); 331 | assert(!machine.isActive()); 332 | assert(!machine.isActive()); 333 | 334 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 335 | 336 | machine.react(Action{}); 337 | 338 | const Status reacted1[] = { 339 | status(Event::ReactionRequest), 340 | status(Event::Reaction), 341 | status(Event::ReactionRequest), 342 | }; 343 | _.assertHistory(reacted1); 344 | 345 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 346 | 347 | machine.update(); 348 | const Status update1[] = { 349 | status(Event::Update), 350 | status(Event::Transition), 351 | status(Event::Update), 352 | status(Event::Transition), 353 | 354 | status(Event::Restart), 355 | 356 | status(Event::Substitute), 357 | status(Event::Substitute), 358 | 359 | status(Event::Leave), 360 | 361 | status(Event::Enter), 362 | status(Event::Enter), 363 | }; 364 | _.assertHistory(update1); 365 | 366 | assert(!machine.isResumable()); 367 | assert( machine.isResumable()); 368 | assert(!machine.isResumable()); 369 | assert(!machine.isResumable()); 370 | assert(!machine.isResumable()); 371 | assert(!machine.isResumable()); 372 | assert(!machine.isResumable()); 373 | assert(!machine.isResumable()); 374 | assert(!machine.isResumable()); 375 | assert(!machine.isResumable()); 376 | assert(!machine.isResumable()); 377 | assert(!machine.isResumable()); 378 | 379 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 380 | 381 | machine.react(Action{}); 382 | 383 | const Status reacted2[] = { 384 | status(Event::ReactionRequest), 385 | status(Event::Reaction), 386 | status(Event::ReactionRequest), 387 | status(Event::ReactionRequest), 388 | status(Event::Reaction), 389 | }; 390 | _.assertHistory(reacted2); 391 | 392 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 393 | 394 | machine.update(); 395 | const Status update2[] = { 396 | status(Event::Update), 397 | status(Event::Transition), 398 | status(Event::Update), 399 | status(Event::Transition), 400 | status(Event::Restart), 401 | 402 | status(Event::Update), 403 | 404 | status(Event::Substitute), 405 | status(Event::Substitute), 406 | status(Event::Substitute), 407 | status(Event::Substitute), 408 | status(Event::Substitute), 409 | 410 | status(Event::Leave), 411 | status(Event::Leave), 412 | status(Event::Leave), 413 | 414 | status(Event::Enter), 415 | status(Event::Enter), 416 | status(Event::Enter), 417 | status(Event::Enter), 418 | status(Event::Enter), 419 | }; 420 | _.assertHistory(update2); 421 | 422 | assert(!machine.isActive()); 423 | assert(!machine.isActive()); 424 | assert(!machine.isActive()); 425 | assert(!machine.isActive()); 426 | assert(!machine.isActive()); 427 | assert( machine.isActive()); 428 | assert( machine.isActive()); 429 | assert( machine.isActive()); 430 | assert(!machine.isActive()); 431 | assert( machine.isActive()); 432 | assert(!machine.isActive()); 433 | assert( machine.isActive()); 434 | 435 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 436 | 437 | machine.react(Action{}); 438 | 439 | const Status reacted3[] = { 440 | status(Event::ReactionRequest), 441 | status(Event::Reaction), 442 | status(Event::ReactionRequest), 443 | status(Event::ReactionRequest), 444 | status(Event::ReactionRequest), 445 | status(Event::ReactionRequest), 446 | }; 447 | _.assertHistory(reacted3); 448 | 449 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 450 | 451 | machine.update(); 452 | const Status update3[] = { 453 | status(Event::Update), 454 | status(Event::Transition), 455 | status(Event::Update), 456 | status(Event::Transition), 457 | status(Event::Update), 458 | status(Event::Transition), 459 | status(Event::Update), 460 | status(Event::Transition), 461 | status(Event::Update), 462 | status(Event::Transition), 463 | 464 | status(Event::Resume), 465 | 466 | status(Event::Substitute), 467 | status(Event::Substitute), 468 | status(Event::Substitute), 469 | 470 | status(Event::Leave), 471 | status(Event::Leave), 472 | status(Event::Leave), 473 | status(Event::Leave), 474 | status(Event::Leave), 475 | 476 | status(Event::Enter), 477 | status(Event::Enter), 478 | status(Event::Enter), 479 | }; 480 | _.assertHistory(update3); 481 | 482 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 483 | 484 | machine.update(); 485 | const Status update4[] = { 486 | status(Event::Update), 487 | status(Event::Transition), 488 | status(Event::Update), 489 | status(Event::Transition), 490 | status(Event::Resume), 491 | status(Event::Update), 492 | 493 | status(Event::Substitute), 494 | status(Event::Substitute), 495 | status(Event::Substitute), 496 | status(Event::Substitute), 497 | status(Event::Substitute), 498 | 499 | status(Event::Leave), 500 | status(Event::Leave), 501 | status(Event::Leave), 502 | 503 | status(Event::Enter), 504 | status(Event::Enter), 505 | status(Event::Enter), 506 | status(Event::Enter), 507 | status(Event::Enter), 508 | }; 509 | _.assertHistory(update4); 510 | 511 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 512 | 513 | machine.update(); 514 | const Status update5[] = { 515 | status(Event::Update), 516 | status(Event::Transition), 517 | status(Event::Update), 518 | status(Event::Transition), 519 | status(Event::Update), 520 | status(Event::Transition), 521 | status(Event::Update), 522 | status(Event::Transition), 523 | status(Event::Update), 524 | status(Event::Transition), 525 | 526 | status(Event::Restart), 527 | 528 | status(Event::Substitute), 529 | status(Event::Resume), 530 | }; 531 | _.assertHistory(update5); 532 | 533 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 534 | 535 | machine.update(); 536 | const Status update6[] = { 537 | status(Event::Update), 538 | status(Event::Transition), 539 | status(Event::Update), 540 | status(Event::Transition), 541 | status(Event::Update), 542 | status(Event::Transition), 543 | status(Event::Update), 544 | status(Event::Transition), 545 | status(Event::Update), 546 | status(Event::Transition), 547 | 548 | status(Event::Schedule), 549 | status(Event::Resume), 550 | 551 | status(Event::Substitute), 552 | status(Event::Substitute), 553 | status(Event::Substitute), 554 | 555 | status(Event::Leave), 556 | status(Event::Leave), 557 | status(Event::Leave), 558 | status(Event::Leave), 559 | status(Event::Leave), 560 | 561 | status(Event::Enter), 562 | status(Event::Enter), 563 | status(Event::Enter), 564 | }; 565 | _.assertHistory(update6); 566 | 567 | // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 568 | } 569 | 570 | const Status destroyed[] = { 571 | status(Event::Leave), 572 | status(Event::Leave), 573 | status(Event::Leave), 574 | }; 575 | _.assertHistory(destroyed); 576 | 577 | return 0; 578 | } 579 | 580 | //////////////////////////////////////////////////////////////////////////////// 581 | --------------------------------------------------------------------------------