diff --git a/include/boost/sml.hpp b/include/boost/sml.hpp index 736e3bca..6ad7b5bf 100644 --- a/include/boost/sml.hpp +++ b/include/boost/sml.hpp @@ -1062,6 +1062,10 @@ struct internal_event { struct anonymous : internal_event { constexpr static auto c_str() { return "anonymous"; } }; +template +struct completion : internal_event { + constexpr static auto c_str() { return "completion"; } +}; template struct on_entry : internal_event, entry_exit { constexpr static auto c_str() { return "on_entry"; } @@ -1497,6 +1501,9 @@ struct get_event_mapping_impl_helper, TMappings> template struct get_event_mapping_impl_helper, TMappings> : decltype(get_event_mapping_impl>((TMappings *)0)) {}; +template +struct get_event_mapping_impl_helper, TMappings> + : decltype(get_event_mapping_impl>((TMappings *)0)) {}; template struct get_event_mapping_impl_helper : decltype(get_event_mapping_impl((TMappings *)0)) {}; template @@ -1842,18 +1849,41 @@ struct sm_impl : aux::conditional_t>(event, deps, subs); bool queued_handled = true; do { do { - while (process_internal_events(anonymous{}, deps, subs)) { - } changed = (old != current_state_[0]); old = current_state_[0]; } while (process_defer_events(deps, subs, changed, aux::type_wrapper>{}, events_t{})); } while (process_queued_events(deps, subs, queued_handled, aux::type_wrapper>{}, events_t{})); return handled && queued_handled; } + template + constexpr bool process_completion_event(TDeps &deps, TSubs &subs, aux::true_type) { + return process_internal_events(completion{}, deps, subs); + } + template + constexpr bool process_completion_event(TDeps &, TSubs &, aux::false_type) { + return false; + } + template + constexpr bool process_post_event_step(TDeps &deps, TSubs &subs) { + return process_completion_event( + deps, subs, typename aux::is_base_of, events_ids_t>::type{}) || + process_internal_events(anonymous{}, deps, subs); + } + template + constexpr void process_post_events(TDeps &deps, TSubs &subs) { + while (process_post_event_step(deps, subs)) { + } + } + template + constexpr bool process_event_and_post_events(const TEvent &event, TDeps &deps, TSubs &subs) { + const bool handled = process_internal_events(event, deps, subs); + process_post_events(deps, subs); + return handled; + } constexpr void initialize(const aux::type_list<> &) {} template constexpr void initialize(const aux::type_list &) { @@ -2027,7 +2057,7 @@ struct sm_impl : aux::conditional_t constexpr bool process_event_no_defer(TDeps &deps, TSubs &subs, const void *data) { const auto &event = *static_cast(data); - bool handled = process_internal_events(event, deps, subs); + bool handled = process_event_and_post_events>(event, deps, subs); if (handled && defer_again_) { ++defer_it_; } else { @@ -2068,13 +2098,7 @@ struct sm_impl : aux::conditional_t constexpr bool process_event_no_queue(TDeps &deps, TSubs &subs, const void *data) { const auto &event = *static_cast(data); - policies::log_process_event(aux::type_wrapper{}, deps, event); -#if BOOST_SML_DISABLE_EXCEPTIONS - return process_event_impl>(event, deps, subs, states_t{}, - aux::make_index_sequence{}); -#else - return process_event_except_impl>(event, deps, subs, has_exceptions{}); -#endif + return process_event_and_post_events>(event, deps, subs); } template bool process_queued_events(TDeps &deps, TSubs &subs, bool &queued_handled, const aux::type_wrapper &, const aux::type_list &) { @@ -3229,6 +3253,8 @@ template front::event> unexpected_event __BOOST_SML_VT_INIT; template front::event> exception __BOOST_SML_VT_INIT; +template +using completion = back::completion; using anonymous = back::anonymous; using initial = back::initial; #if !(defined(_MSC_VER) && !defined(__clang__)) diff --git a/test/ft/actions_process_n_defer.cpp b/test/ft/actions_process_n_defer.cpp index 53cf62f5..f79fdb46 100644 --- a/test/ft/actions_process_n_defer.cpp +++ b/test/ft/actions_process_n_defer.cpp @@ -118,6 +118,80 @@ test process_n_defer_again = [] { expect(calls == "|s3_entry|e1|e1|e1"); }; +test process_queue_runs_completion_for_popped_event_type = [] { + struct trigger {}; + struct queued1 {}; + struct queued2 {}; + struct q0 {}; + struct q1 {}; + struct q2 {}; + struct q3 {}; + struct done {}; + struct wrong {}; + + struct c { + auto operator()() const { + using namespace sml; + auto q0_state = state; + auto q1_state = state; + auto q2_state = state; + auto q3_state = state; + auto done_state = state; + auto wrong_state = state; + // clang-format off + return make_transition_table( + *q0_state + event / (process(queued1{}), process(queued2{})) = q1_state + , q1_state + event = q2_state + , q2_state + event> = q3_state + , q2_state + event = wrong_state + , q3_state + event = done_state + ); + // clang-format on + } + }; + + sml::sm> sm{}; + expect(sm.process_event(trigger{})); + expect(sm.is(sml::state)); + expect(!sm.is(sml::state)); +}; + +test defer_queue_runs_completion_for_popped_event_type = [] { + struct deferred {}; + struct release {}; + struct d0 {}; + struct d1 {}; + struct d2 {}; + struct done {}; + struct wrong {}; + + struct c { + auto operator()() const { + using namespace sml; + auto d0_state = state; + auto d1_state = state; + auto d2_state = state; + auto done_state = state; + auto wrong_state = state; + // clang-format off + return make_transition_table( + *d0_state + event / defer + , d0_state + event = d1_state + , d1_state + event = d2_state + , d2_state + event> = done_state + , d2_state + event> = wrong_state + ); + // clang-format on + } + }; + + sml::sm, sml::defer_queue> sm{}; + expect(sm.process_event(deferred{})); + expect(sm.process_event(release{})); + expect(sm.is(sml::state)); + expect(!sm.is(sml::state)); +}; + template using MinimalStaticDeque10 = MinimalStaticDeque; @@ -147,4 +221,4 @@ test mix_process_n_defer_at_init_static_queue = [] { sml::sm, sml::defer_queue> sm{}; expect(sm.is(sml::X)); -}; \ No newline at end of file +}; diff --git a/test/ft/transitions.cpp b/test/ft/transitions.cpp index 47c30dcf..b8996ed8 100644 --- a/test/ft/transitions.cpp +++ b/test/ft/transitions.cpp @@ -88,6 +88,30 @@ test anonymous_transition = [] { expect(static_cast(sm).a_called); }; +test completion_transition_runs_before_anonymous = [] { + struct c { + auto operator()() noexcept { + using namespace sml; + // clang-format off + return make_transition_table( + *idle + event = s1 + ,s1 + event> / [this] { calls += "completion|"; } = s2 + ,s1 / [this] { calls += "anonymous_s1|"; } = s3 + ,s2 / [this] { calls += "anonymous_s2|"; } = s4 + ); + // clang-format on + } + + std::string calls{}; + }; + + sml::sm sm{}; + expect(sm.process_event(e1{})); + expect(sm.is(s4)); + expect(!sm.is(s3)); + expect(static_cast(sm).calls == "completion|anonymous_s2|"); +}; + test subsequent_anonymous_transitions = [] { struct c { auto operator()() noexcept {