From c2bd4e61bde6d978885f4aba34a07bf3554f58b2 Mon Sep 17 00:00:00 2001 From: Amar Mahmutbegovic Date: Sat, 11 Jan 2025 18:46:30 +0100 Subject: [PATCH] add boost::sml example --- Chapter17/fsm/CMakeLists.txt | 3 +- Chapter17/fsm/app/src/main_fsm_boost_sml.cpp | 92 + Chapter17/fsm/app/src/main_fsm_simple.cpp | 4 +- .../fsm/app/src/main_fsm_state_pattern.cpp | 174 + Chapter17/fsm/libs/boost/sml.hpp | 2900 +++++++++++++++++ 5 files changed, 3170 insertions(+), 3 deletions(-) create mode 100644 Chapter17/fsm/app/src/main_fsm_boost_sml.cpp create mode 100644 Chapter17/fsm/app/src/main_fsm_state_pattern.cpp create mode 100644 Chapter17/fsm/libs/boost/sml.hpp diff --git a/Chapter17/fsm/CMakeLists.txt b/Chapter17/fsm/CMakeLists.txt index 3d9410c..8e88ab0 100644 --- a/Chapter17/fsm/CMakeLists.txt +++ b/Chapter17/fsm/CMakeLists.txt @@ -89,6 +89,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/hal/adc/inc ${CMAKE_SOURCE_DIR}/cstdlib_support ${CMAKE_SOURCE_DIR}/util/inc + ${CMAKE_SOURCE_DIR}/libs/boost ) set(EXECUTABLE ${PROJECT_NAME}.elf) @@ -130,8 +131,6 @@ target_link_options( -mthumb ${LIB_SPECS} -lnosys - -u - _printf_float -lc -lm -Wl,--no-warn-rwx-segments diff --git a/Chapter17/fsm/app/src/main_fsm_boost_sml.cpp b/Chapter17/fsm/app/src/main_fsm_boost_sml.cpp new file mode 100644 index 0000000..6fe8b76 --- /dev/null +++ b/Chapter17/fsm/app/src/main_fsm_boost_sml.cpp @@ -0,0 +1,92 @@ +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +#include + +#include "sml.hpp" + +namespace sml = boost::sml; + +struct ble_button_pressed{}; +struct connection_request{}; +struct timer_expired{}; + +constexpr auto start_advertising = [](){ + printf("Action: start_advertising()\n"); +}; + +constexpr auto stop_advertising = [](){ + printf("Action: stop_advertising()\n"); +}; + +constexpr auto disconnect = [](){ + printf("Action: disconnect()\n"); +}; + +struct ble_fsm { + auto operator()() const { + using namespace sml; + /** + * Initial state: *initial_state + * Transition DSL: src_state + event [ guard ] / action = dst_state + */ + return make_transition_table( + *"idle"_s + event / start_advertising = "advertising"_s, + "advertising"_s + event = "connected"_s, + "advertising"_s + event / stop_advertising = "idle"_s, + "connected"_s + event / disconnect = "idle"_s + ); + } +}; + +int main() +{ + hal::init(); + + hal::uart_stm32 uart(USART2); + uart.init(); + + retarget::set_stdio_uart(&uart); + + using namespace sml; + + sm my_ble_fsm{}; + + const auto print_current_state = [&]() { + printf("Current State: "); + if(my_ble_fsm.is("idle"_s)) { + printf("idle\n"); + } + if(my_ble_fsm.is("advertising"_s)) { + printf("advertising\n"); + } + if(my_ble_fsm.is("connected"_s)) { + printf("connected\n"); + } + }; + + print_current_state(); + + my_ble_fsm.process_event(ble_button_pressed{}); + print_current_state(); + + my_ble_fsm.process_event(connection_request{}); + print_current_state(); + + my_ble_fsm.process_event(ble_button_pressed{}); + print_current_state(); + + while(true) + { + } +} diff --git a/Chapter17/fsm/app/src/main_fsm_simple.cpp b/Chapter17/fsm/app/src/main_fsm_simple.cpp index 029e5c4..98fe6ff 100644 --- a/Chapter17/fsm/app/src/main_fsm_simple.cpp +++ b/Chapter17/fsm/app/src/main_fsm_simple.cpp @@ -74,7 +74,8 @@ private: } }; -static const char* state_to_string(ble_state state) { +namespace { +const char* state_to_string(ble_state state) { switch (state) { case ble_state::idle: return "idle"; case ble_state::advertising: return "advertising"; @@ -82,6 +83,7 @@ static const char* state_to_string(ble_state state) { default: return "unknown"; } } +} int main() { diff --git a/Chapter17/fsm/app/src/main_fsm_state_pattern.cpp b/Chapter17/fsm/app/src/main_fsm_state_pattern.cpp new file mode 100644 index 0000000..aa64523 --- /dev/null +++ b/Chapter17/fsm/app/src/main_fsm_state_pattern.cpp @@ -0,0 +1,174 @@ +#include +#include + +#include + +#include + +#include +#include +#include + +#include + +#include "etl/vector.h" + +enum class ble_state { + idle, + advertising, + connected +}; + +enum class ble_event { + ble_button_pressed, + connection_request, + timer_expired +}; + +class state { +public: + virtual ble_state handle_event(ble_event event) = 0; + virtual ble_state get_state_enum() = 0; +}; + +class idle : public state{ +public: + ble_state handle_event(ble_event event) { + if (event == ble_event::ble_button_pressed) { + start_advertising(); + return ble_state::advertising; + } + return get_state_enum(); + } + ble_state get_state_enum() { + return ble_state::idle; + } +private: + void start_advertising() { + printf("Action: start_advertising()\n"); + } +}; + +class advertising : public state{ +public: + ble_state handle_event(ble_event event) { + if (event == ble_event::connection_request) { + return ble_state::connected; + } + if (event == ble_event::timer_expired) { + stop_advertising(); + return ble_state::idle; + } + return get_state_enum(); + } + ble_state get_state_enum() { + return ble_state::advertising; + } +private: + void stop_advertising() { + printf("Action: stop_advertising()\n"); + } +}; + +class connected : public state{ +public: + ble_state handle_event(ble_event event) { + if (event == ble_event::ble_button_pressed) { + disconnect(); + return ble_state::idle; + } + return get_state_enum(); + } + ble_state get_state_enum() { + return ble_state::connected; + } +private: + void disconnect() { + printf("Action: disconnect()\n"); + } +}; + +class ble_fsm { +public: + void handle_event(ble_event event) { + if(auto the_state = get_the_state(current_state_)) { + current_state_ = the_state->handle_event(event); + } + } + + ble_state get_state() const { + return current_state_; + } + + void add_state(state *the_state) { + states_.push_back(the_state); + } + +private: + std::vector states_; + ble_state current_state_ = ble_state::idle; + + state* get_the_state(ble_state state_enum) { + const auto is_state_enum = [&] (state *the_state) { + return the_state->get_state_enum() == state_enum; + }; + auto it = std::find_if(states_.begin(), states_.end(), is_state_enum); + + if(it != states_.end()) { + return *it; + } + return nullptr; + } +}; + +namespace { +const char* state_to_string(ble_state state) { + switch (state) { + case ble_state::idle: return "idle"; + case ble_state::advertising: return "advertising"; + case ble_state::connected: return "connected"; + default: return "unknown"; + } +} +} + + +int main() +{ + hal::init(); + + hal::uart_stm32 uart(USART2); + uart.init(); + + retarget::set_stdio_uart(&uart); + + ble_fsm my_ble_fsm; + + idle idle_s; + advertising advertising_s; + connected connected_s; + + my_ble_fsm.add_state(&idle_s); + my_ble_fsm.add_state(&advertising_s); + my_ble_fsm.add_state(&connected_s); + + const auto print_current_state = [&]() { + printf("Current State: %s\n", state_to_string(my_ble_fsm.get_state())); + }; + + print_current_state(); + + my_ble_fsm.handle_event(ble_event::ble_button_pressed); + print_current_state(); + + my_ble_fsm.handle_event(ble_event::connection_request); + print_current_state(); + + my_ble_fsm.handle_event(ble_event::ble_button_pressed); + print_current_state(); + + + while(true) + { + } +} diff --git a/Chapter17/fsm/libs/boost/sml.hpp b/Chapter17/fsm/libs/boost/sml.hpp new file mode 100644 index 0000000..ca66764 --- /dev/null +++ b/Chapter17/fsm/libs/boost/sml.hpp @@ -0,0 +1,2900 @@ +// +// Copyright (c) 2016-2024 Kris Jusiak (kris at jusiak dot net) +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#ifndef BOOST_SML_HPP +#define BOOST_SML_HPP +#if (__cplusplus < 201305L && _MSC_VER < 1900) +#error "[Boost::ext].SML requires C++14 support (Clang-3.4+, GCC-5.1+, MSVC-2015+)" +#else +#if defined(__ICCARM__) && __IAR_SYSTEMS_ICC__ < 8 +#error "[Boost::ext].SML requires C++14 support (IAR C/C++ ARM 8.1+)" +#endif +#define BOOST_SML_VERSION 1'1'11 +#define BOOST_SML_NAMESPACE_BEGIN \ + namespace boost { \ + inline namespace ext { \ + namespace sml { \ + inline namespace v1_1_11 { +#define BOOST_SML_NAMESPACE_END \ + } \ + } \ + } \ + } +#if defined(__clang__) +#define __BOOST_SML_UNUSED __attribute__((unused)) +#define __BOOST_SML_VT_INIT \ + {} +#if !defined(BOOST_SML_CFG_DISABLE_MIN_SIZE) +#define __BOOST_SML_ZERO_SIZE_ARRAY(...) __VA_ARGS__ _[0] +#else +#define __BOOST_SML_ZERO_SIZE_ARRAY(...) +#endif +#define __BOOST_SML_ZERO_SIZE_ARRAY_CREATE(...) +#define __BOOST_SML_TEMPLATE_KEYWORD template +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-string-literal-operator-template" +#pragma clang diagnostic ignored "-Wzero-length-array" +#elif defined(__GNUC__) +#if !defined(__has_builtin) +#define __BOOST_SML_DEFINED_HAS_BUILTIN +#define __has_builtin(...) 0 +#endif +#define __BOOST_SML_UNUSED __attribute__((unused)) +#define __BOOST_SML_VT_INIT \ + {} +#if !defined(BOOST_SML_CFG_DISABLE_MIN_SIZE) +#define __BOOST_SML_ZERO_SIZE_ARRAY(...) __VA_ARGS__ _[0]{} +#else +#define __BOOST_SML_ZERO_SIZE_ARRAY(...) +#endif +#define __BOOST_SML_ZERO_SIZE_ARRAY_CREATE(...) __VA_ARGS__ ? __VA_ARGS__ : 1 +#define __BOOST_SML_TEMPLATE_KEYWORD template +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpedantic" +#if defined(__GNUC__) && (__GNUC__ >= 10) +#pragma GCC diagnostic ignored "-Wsubobject-linkage" +#endif +#elif defined(_MSC_VER) && !defined(__clang__) +#define __BOOST_SML_DEFINED_HAS_BUILTIN +#define __has_builtin(...) __has_builtin##__VA_ARGS__ +#define __has_builtin__make_integer_seq(...) 1 +#define __BOOST_SML_UNUSED +#define __BOOST_SML_VT_INIT +#define __BOOST_SML_ZERO_SIZE_ARRAY(...) +#define __BOOST_SML_ZERO_SIZE_ARRAY_CREATE(...) __VA_ARGS__ ? __VA_ARGS__ : 1 +#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER >= 1910 // MSVC 2017 +#define __BOOST_SML_TEMPLATE_KEYWORD template +#else +#define __BOOST_SML_TEMPLATE_KEYWORD +#endif +#pragma warning(disable : 4503) +#pragma warning(disable : 4200) +#elif defined(__ICCARM__) +#if !defined(__has_builtin) +#define __BOOST_SML_DEFINED_HAS_BUILTIN +#define __has_builtin(...) 0 +#endif +/* Needs IAR language extensions */ +#define __BOOST_SML_UNUSED __attribute__((unused)) +#define __BOOST_SML_VT_INIT \ + {} +#define __BOOST_SML_ZERO_SIZE_ARRAY(...) +#define __BOOST_SML_ZERO_SIZE_ARRAY_CREATE(...) __VA_ARGS__ ? __VA_ARGS__ : 1 +#define __BOOST_SML_TEMPLATE_KEYWORD template +#endif +BOOST_SML_NAMESPACE_BEGIN +#define __BOOST_SML_REQUIRES(...) typename aux::enable_if<__VA_ARGS__, int>::type = 0 +namespace aux { +using byte = unsigned char; +struct none_type {}; +template +struct type {}; +template +struct non_type {}; +template +struct pair {}; +template +struct type_list { + using type = type_list; +}; +template +struct bool_list { + using type = bool_list; +}; +template +struct inherit : Ts... { + using type = inherit; +}; +template +struct identity { + using type = T; +}; +template +T &&declval(); +template +struct integral_constant { + using type = integral_constant; + static constexpr T value = V; +}; +using true_type = integral_constant; +using false_type = integral_constant; +template +using void_t = void; +template +struct always : true_type {}; +template +struct never : false_type {}; +namespace detail { +template +struct conditional; +template <> +struct conditional { + template + using fn = T; +}; +template <> +struct conditional { + template + using fn = T; +}; +} // namespace detail +template +struct conditional { + using type = typename detail::conditional::template fn; +}; +template +using conditional_t = typename detail::conditional::template fn; +template +struct enable_if {}; +template +struct enable_if { + using type = T; +}; +template +using enable_if_t = typename enable_if::type; +template +struct is_same : false_type {}; +template +struct is_same : true_type {}; +template +#if defined(_MSC_VER) && !defined(__clang__) +struct is_base_of : integral_constant { +}; +#else +using is_base_of = integral_constant; +#endif +template +decltype(T(declval()...), true_type{}) test_is_constructible(int); +template +false_type test_is_constructible(...); +template +#if defined(_MSC_VER) && !defined(__clang__) +struct is_constructible : decltype(test_is_constructible(0)) { +}; +#else +using is_constructible = decltype(test_is_constructible(0)); +#endif +template +struct is_empty_base : T { + U _; +}; +template +struct is_empty : aux::integral_constant) == sizeof(none_type)> {}; +template +struct function_traits; +template +struct function_traits { + using args = type_list; +}; +template +struct function_traits { + using args = type_list; +}; +template +struct function_traits { + using args = type_list; +}; +template +struct function_traits { + using args = type_list; +}; +#if defined(__cpp_noexcept_function_type) +template +struct function_traits { + using args = type_list; +}; +template +struct function_traits { + using args = type_list; +}; +template +struct function_traits { + using args = type_list; +}; +template +struct function_traits { + using args = type_list; +}; +#endif +template +using function_traits_t = typename function_traits::args; +template +struct remove_const { + using type = T; +}; +template +struct remove_const { + using type = T; +}; +template +using remove_const_t = typename remove_const::type; +template +struct remove_reference { + using type = T; +}; +template +struct remove_reference { + using type = T; +}; +template +struct remove_reference { + using type = T; +}; +template +using remove_reference_t = typename remove_reference::type; +template +struct remove_pointer { + using type = T; +}; +template +struct remove_pointer { + using type = T; +}; +template +using remove_pointer_t = typename remove_pointer::type; +} // namespace aux +namespace aux { +using swallow = int[]; +template +struct index_sequence { + using type = index_sequence; +}; +#if __has_builtin(__make_integer_seq) +template +struct integer_sequence; +template +struct integer_sequence { + using type = index_sequence; +}; +template +struct make_index_sequence_impl { + using type = typename __make_integer_seq::type; +}; +#else +template +struct concat; +template +struct concat, index_sequence> : index_sequence {}; +template +struct make_index_sequence_impl + : concat::type, typename make_index_sequence_impl::type>::type {}; +template <> +struct make_index_sequence_impl<0> : index_sequence<> {}; +template <> +struct make_index_sequence_impl<1> : index_sequence<0> {}; +#endif +template +using make_index_sequence = typename make_index_sequence_impl::type; +template +struct join { + using type = type_list<>; +}; +template +struct join { + using type = T; +}; +template +struct join> : type_list {}; +template +struct join, type_list> : type_list {}; +template +struct join, type_list, type_list> : type_list {}; +template +struct join, type_list, Ts...> : join, Ts...> {}; +template +struct join, type_list, type_list, type_list, type_list, type_list, + type_list, type_list, type_list, type_list, type_list, type_list, + type_list, type_list, type_list, type_list, type_list, Us...> + : join, + Us...> {}; +template +using join_t = typename join::type; +template +struct unique_impl; +template +struct unique_impl, T, Ts...> : conditional_t, inherit...>>::value, + unique_impl, Ts...>, unique_impl, Ts...>> { +}; +template +struct unique_impl> : type_list {}; +template +struct unique : unique_impl, Ts...> {}; +template +struct unique : type_list {}; +template +using unique_t = typename unique::type; +template +struct is_unique; +template +struct is_unique : true_type {}; +template +struct is_unique, T, Ts...> + : conditional_t, inherit...>>::value, false_type, is_unique, Ts...>> {}; +template +using is_unique_t = is_unique, Ts...>; +template