diff --git a/include/boost/sml.hpp b/include/boost/sml.hpp index e974c0f3..7065e66e 100644 --- a/include/boost/sml.hpp +++ b/include/boost/sml.hpp @@ -2925,7 +2925,21 @@ constexpr void update_composite_states(TSubs &subs, aux::false_type, Ts &&...) { using sm_impl_t = aux::apply_t; update_composite_states(subs, aux::false_type{}, Ts{}...); } - }); + }); +} +constexpr void clear_defer_queue(back::no_policy &) {} +template +constexpr void clear_defer_queue(TQueue &queue) { + queue.erase(queue.begin(), queue.end()); +} +template +constexpr void clear_sub_sm_defer_state(TSubs &subs) { + auto &sub_sm = back::sub_sm>::get(&subs); + clear_defer_queue(sub_sm.defer_); + sub_sm.defer_processing_ = decltype(sub_sm.defer_processing_){}; + sub_sm.defer_again_ = decltype(sub_sm.defer_again_){}; + sub_sm.defer_it_ = {}; + sub_sm.defer_end_ = {}; } template constexpr void update_current_state(SM &, TDeps &deps, TSubs &, typename SM::state_t ¤t_state, @@ -2935,6 +2949,15 @@ constexpr void update_current_state(SM &, TDeps &deps, TSubs &, typename SM::sta aux::string{}); current_state = new_state; } +template )> +constexpr void update_current_state(SM &, TDeps &deps, TSubs &subs, typename SM::state_t ¤t_state, + const typename SM::state_t &new_state, const state> &, const TDstState &) { + back::policies::log_state_change(aux::type_wrapper{}, deps, aux::string{}, + aux::string{}); + current_state = new_state; + clear_sub_sm_defer_state(subs); +} template constexpr void update_current_state(SM &, TDeps &deps, TSubs &subs, typename SM::state_t ¤t_state, const typename SM::state_t &new_state, const TSrcState &, const state> &) { @@ -2944,6 +2967,17 @@ constexpr void update_current_state(SM &, TDeps &deps, TSubs &subs, typename SM: update_composite_states>(subs, typename back::sm_impl::has_history_states{}, typename back::sm_impl::history_states_t{}); } +template +constexpr void update_current_state(SM &, TDeps &deps, TSubs &subs, typename SM::state_t ¤t_state, + const typename SM::state_t &new_state, const state> &, + const state> &) { + back::policies::log_state_change(aux::type_wrapper{}, deps, aux::string{}, + aux::string{}); + current_state = new_state; + clear_sub_sm_defer_state(subs); + update_composite_states>(subs, typename back::sm_impl::has_history_states{}, + typename back::sm_impl::history_states_t{}); +} template struct transition, state, front::event, G, A> { static constexpr auto initial = state::initial; diff --git a/test/ft/CMakeLists.txt b/test/ft/CMakeLists.txt index 267a0c89..6c1750b9 100644 --- a/test/ft/CMakeLists.txt +++ b/test/ft/CMakeLists.txt @@ -48,6 +48,9 @@ add_test(test_fwd test_fwd) add_executable(test_history history.cpp) add_test(test_history test_history) +add_executable(test_issue_253 issue_253.cpp) +add_test(test_issue_253 test_issue_253) + add_executable(test_guards guards.cpp) add_test(test_guards test_guards) diff --git a/test/ft/issue_253.cpp b/test/ft/issue_253.cpp new file mode 100644 index 00000000..28d5fe6a --- /dev/null +++ b/test/ft/issue_253.cpp @@ -0,0 +1,80 @@ +// +// Copyright (c) 2016-2020 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) +// +#include +#include +#include + +namespace sml = boost::sml; + +test issue_253_nested_defer_queue_is_cleared_on_exit = [] { + struct issue_253_enter {}; + struct issue_253_exit {}; + struct issue_253_to_s2 {}; + struct issue_253_to_s1 {}; + struct issue_253_e1 {}; + struct issue_253_outer_idle {}; + + struct issue_253_counters { + int nested_calls = 0; + int outer_calls = 0; + }; + + struct issue_253_nested { + auto operator()() const { + using namespace sml; + const auto issue_253_s1 = sml::state; + const auto issue_253_s2 = sml::state; + // clang-format off + return make_transition_table( + *issue_253_s1 + event = issue_253_s2 + , issue_253_s2 + event / defer + , issue_253_s2 + event = issue_253_s1 + , issue_253_s1 + event / [](issue_253_counters& counters) { ++counters.nested_calls; } + ); + // clang-format on + } + }; + + struct issue_253_outer { + auto operator()() const { + using namespace sml; + const auto issue_253_idle = sml::state; + const auto issue_253_nested_state = sml::state; + // clang-format off + return make_transition_table( + *issue_253_idle + event = issue_253_nested_state + , issue_253_nested_state + event = issue_253_idle + , issue_253_idle + event / [](issue_253_counters& counters) { ++counters.outer_calls; } + ); + // clang-format on + } + }; + + issue_253_counters counters{}; + sml::sm, sml::process_queue> sm{counters}; + + expect(sm.process_event(issue_253_enter{})); + expect(sm.process_event(issue_253_to_s2{})); + expect(sm.process_event(issue_253_e1{})); + expect(sm.process_event(issue_253_to_s1{})); + expect(sm.process_event(issue_253_exit{})); + expect(1 == counters.nested_calls); + expect(0 == counters.outer_calls); + + expect(sm.process_event(issue_253_e1{})); + expect(1 == counters.outer_calls); + + expect(sm.process_event(issue_253_enter{})); + expect(sm.process_event(issue_253_to_s2{})); + expect(sm.process_event(issue_253_to_s1{})); + expect(sm.process_event(issue_253_exit{})); + expect(1 == counters.nested_calls); + + expect(sm.process_event(issue_253_e1{})); + expect(2 == counters.outer_calls); +};