Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 37 additions & 11 deletions include/boost/sml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,10 @@ struct internal_event {
struct anonymous : internal_event {
constexpr static auto c_str() { return "anonymous"; }
};
template <class TEvent>
struct completion : internal_event {
constexpr static auto c_str() { return "completion"; }
};
template <class T, class TEvent = T>
struct on_entry : internal_event, entry_exit {
constexpr static auto c_str() { return "on_entry"; }
Expand Down Expand Up @@ -1497,6 +1501,9 @@ struct get_event_mapping_impl_helper<on_entry<T1, T2>, TMappings>
template <class T1, class T2, class TMappings>
struct get_event_mapping_impl_helper<on_exit<T1, T2>, TMappings>
: decltype(get_event_mapping_impl<on_exit<T1, T2>>((TMappings *)0)) {};
template <class TEvent, class TMappings>
struct get_event_mapping_impl_helper<completion<TEvent>, TMappings>
: decltype(get_event_mapping_impl<completion<TEvent>>((TMappings *)0)) {};
template <class TMappings>
struct get_event_mapping_impl_helper<anonymous, TMappings> : decltype(get_event_mapping_impl<anonymous>((TMappings *)0)) {};
template <class T, class TMappings>
Expand Down Expand Up @@ -1842,18 +1849,41 @@ struct sm_impl : aux::conditional_t<aux::should_not_subclass_statemachine_class<
(void)lock;
bool changed = false;
state_t old = current_state_[0];
bool handled = process_internal_events(event, deps, subs);
bool handled = process_event_and_post_events<get_event_t<TEvent>>(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<defer_queue_t<TEvent>>{}, events_t{}));
} while (process_queued_events(deps, subs, queued_handled, aux::type_wrapper<process_queue_t<TEvent>>{}, events_t{}));
return handled && queued_handled;
}
template <class TOriginEvent, class TDeps, class TSubs>
constexpr bool process_completion_event(TDeps &deps, TSubs &subs, aux::true_type) {
return process_internal_events(completion<TOriginEvent>{}, deps, subs);
}
template <class TOriginEvent, class TDeps, class TSubs>
constexpr bool process_completion_event(TDeps &, TSubs &, aux::false_type) {
return false;
}
template <class TOriginEvent, class TDeps, class TSubs>
constexpr bool process_post_event_step(TDeps &deps, TSubs &subs) {
return process_completion_event<TOriginEvent>(
deps, subs, typename aux::is_base_of<completion<TOriginEvent>, events_ids_t>::type{}) ||
process_internal_events(anonymous{}, deps, subs);
}
template <class TOriginEvent, class TDeps, class TSubs>
constexpr void process_post_events(TDeps &deps, TSubs &subs) {
while (process_post_event_step<TOriginEvent>(deps, subs)) {
}
}
template <class TOriginEvent, class TEvent, class TDeps, class TSubs>
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<TOriginEvent>(deps, subs);
return handled;
}
constexpr void initialize(const aux::type_list<> &) {}
template <class TState>
constexpr void initialize(const aux::type_list<TState> &) {
Expand Down Expand Up @@ -2027,7 +2057,7 @@ struct sm_impl : aux::conditional_t<aux::should_not_subclass_statemachine_class<
template <class TDeps, class TSubs, class TEvent>
constexpr bool process_event_no_defer(TDeps &deps, TSubs &subs, const void *data) {
const auto &event = *static_cast<const TEvent *>(data);
bool handled = process_internal_events(event, deps, subs);
bool handled = process_event_and_post_events<get_event_t<TEvent>>(event, deps, subs);
if (handled && defer_again_) {
++defer_it_;
} else {
Expand Down Expand Up @@ -2068,13 +2098,7 @@ struct sm_impl : aux::conditional_t<aux::should_not_subclass_statemachine_class<
template <class TDeps, class TSubs, class TEvent>
constexpr bool process_event_no_queue(TDeps &deps, TSubs &subs, const void *data) {
const auto &event = *static_cast<const TEvent *>(data);
policies::log_process_event<sm_t>(aux::type_wrapper<logger_t>{}, deps, event);
#if BOOST_SML_DISABLE_EXCEPTIONS
return process_event_impl<get_event_mapping_t<TEvent, mappings>>(event, deps, subs, states_t{},
aux::make_index_sequence<regions>{});
#else
return process_event_except_impl<get_event_mapping_t<TEvent, mappings>>(event, deps, subs, has_exceptions{});
#endif
return process_event_and_post_events<get_event_t<TEvent>>(event, deps, subs);
}
template <class TDeps, class TSubs, class TDeferQueue, class... TEvents>
bool process_queued_events(TDeps &deps, TSubs &subs, bool &queued_handled, const aux::type_wrapper<TDeferQueue> &, const aux::type_list<TEvents...> &) {
Expand Down Expand Up @@ -3229,6 +3253,8 @@ template <class TEvent>
front::event<back::unexpected_event<TEvent>> unexpected_event __BOOST_SML_VT_INIT;
template <class T>
front::event<back::exception<T>> exception __BOOST_SML_VT_INIT;
template <class TEvent>
using completion = back::completion<TEvent>;
using anonymous = back::anonymous;
using initial = back::initial;
#if !(defined(_MSC_VER) && !defined(__clang__))
Expand Down
76 changes: 75 additions & 1 deletion test/ft/actions_process_n_defer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<q0>;
auto q1_state = state<q1>;
auto q2_state = state<q2>;
auto q3_state = state<q3>;
auto done_state = state<done>;
auto wrong_state = state<wrong>;
// clang-format off
return make_transition_table(
*q0_state + event<trigger> / (process(queued1{}), process(queued2{})) = q1_state
, q1_state + event<queued1> = q2_state
, q2_state + event<completion<queued1>> = q3_state
, q2_state + event<queued2> = wrong_state
, q3_state + event<queued2> = done_state
);
// clang-format on
}
};

sml::sm<c, sml::process_queue<std::queue>> sm{};
expect(sm.process_event(trigger{}));
expect(sm.is(sml::state<done>));
expect(!sm.is(sml::state<wrong>));
};

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<d0>;
auto d1_state = state<d1>;
auto d2_state = state<d2>;
auto done_state = state<done>;
auto wrong_state = state<wrong>;
// clang-format off
return make_transition_table(
*d0_state + event<deferred> / defer
, d0_state + event<release> = d1_state
, d1_state + event<deferred> = d2_state
, d2_state + event<completion<deferred>> = done_state
, d2_state + event<completion<release>> = wrong_state
);
// clang-format on
}
};

sml::sm<c, sml::process_queue<std::queue>, sml::defer_queue<std::deque>> sm{};
expect(sm.process_event(deferred{}));
expect(sm.process_event(release{}));
expect(sm.is(sml::state<done>));
expect(!sm.is(sml::state<wrong>));
};

template <typename T>
using MinimalStaticDeque10 = MinimalStaticDeque<T, 10>;

Expand Down Expand Up @@ -147,4 +221,4 @@ test mix_process_n_defer_at_init_static_queue = [] {

sml::sm<c, sml::process_queue<MinimalStaticQueue10>, sml::defer_queue<MinimalStaticDeque10>> sm{};
expect(sm.is(sml::X));
};
};
24 changes: 24 additions & 0 deletions test/ft/transitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,30 @@ test anonymous_transition = [] {
expect(static_cast<const c&>(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<e1> = s1
,s1 + event<completion<e1>> / [this] { calls += "completion|"; } = s2
,s1 / [this] { calls += "anonymous_s1|"; } = s3
,s2 / [this] { calls += "anonymous_s2|"; } = s4
);
// clang-format on
}

std::string calls{};
};

sml::sm<c> sm{};
expect(sm.process_event(e1{}));
expect(sm.is(s4));
expect(!sm.is(s3));
expect(static_cast<const c&>(sm).calls == "completion|anonymous_s2|");
};

test subsequent_anonymous_transitions = [] {
struct c {
auto operator()() noexcept {
Expand Down
Loading