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
23 changes: 22 additions & 1 deletion doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,27 @@ This design allows you to provide any serializer implementation, but due to the
====


=== Support for conditional event deferral with the `deferred_events` property

In `back` & `back11`, the events mentioned in a state's `deferred_events` property are always deferred.
In `backmp11``, they can be deferred conditionally by defining a method `is_event_deferred` for them:

```cpp
struct MyState : boost::msm::front::state<>
{
using deferred_events = mp11::mp_list<MyEvent>;

template <typename Fsm>
bool is_event_deferred(const MyEvent& event, Fsm& fsm) const
{
// Return false or true to decide
// whether the event shall be deferred.
...
}
};
```


== Resolved issues with respect to `back`

=== Deferring events in orthogonal regions
Expand Down Expand Up @@ -463,7 +484,7 @@ Also the `set_states` API is removed. If setting a state is required, this can s
==== The method `get_state_by_id` is removed

If you really need to get a state by id, please use the universal visitor API to implement the function on your own.
The backmp11 state_machine has a new method to support getting the id of a state in the visitor:
The `backmp11` `state_machine` has a new method to support getting the id of a state in the visitor:

```cpp
template<typename State>
Expand Down
1 change: 1 addition & 0 deletions doc/modules/ROOT/pages/version-history.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* feat(backmp11): Further optimized compilation, with up to 25% faster compile times and less RAM consumption than the 1.90 version
* fix(backmp11): Incorrect FSM type in call on_entry() & on_exit() (https://github.com/boostorg/msm/issues/167[#167])
* feat(backmp11): Merge queued & deferred events into a single event pool (https://github.com/boostorg/msm/issues/168[#168])
* feat(backmp11): Support conditional deferral with the `deferred_events` property (https://github.com/boostorg/msm/issues/155[#155])

== Boost 1.90

Expand Down
45 changes: 26 additions & 19 deletions include/boost/msm/backmp11/detail/favor_runtime_speed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,38 +49,45 @@ struct compile_policy_impl<favor_runtime_speed>
}

template <typename StateMachine, typename Event>
class is_event_deferred_helper
struct is_event_deferred_visitor
{
public:
static bool execute(const StateMachine& sm)
// Apply a pre-filter with the 'has_deferred_events' predicate
// to consider only deferring states
// (this subset needs to be instantiated only once for the SM).
template <typename State>
using predicate = has_deferred_events<State>;
template <typename State>
using predicate2 = has_deferred_event<State, Event>;

is_event_deferred_visitor(const StateMachine& sm, const Event& event)
: sm(sm), event(event)
{
bool result = false;
auto visitor = [&result](const auto& /*state*/)
{
result = true;
};
// Apply a pre-filter with the 'has_deferred_events' predicate,
// since this subset needs to be instantiated only once for the SM.
sm.template visit_if<visit_mode::active_non_recursive,
has_deferred_events, has_deferred_event>(visitor);
return result;
}

private:
using deferring_states = typename StateMachine::deferring_states;
template <typename State>
using has_deferred_event = has_deferred_event<State, Event>;
void operator()(const State& state)
{
result |= state.is_event_deferred(event, sm.get_fsm_argument());
}

const StateMachine& sm;
const Event& event;
bool result{false};
};

template <typename StateMachine, typename Event>
static bool is_event_deferred(const StateMachine& sm, const Event&)
static bool is_event_deferred(const StateMachine& sm, const Event& event)
{
if constexpr (has_deferred_event<
typename StateMachine::deferring_states,
Event>::value)
{
using helper = is_event_deferred_helper<StateMachine, Event>;
return helper::execute(sm);
using visitor_t = is_event_deferred_visitor<StateMachine, Event>;
visitor_t visitor{sm, event};
sm.template visit_if<visit_mode::active_non_recursive,
visitor_t::template predicate,
visitor_t::template predicate2>(visitor);
return visitor.result;
}
else
{
Expand Down
104 changes: 75 additions & 29 deletions include/boost/msm/backmp11/favor_compile_time.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ struct compile_policy_impl<favor_compile_time>
};
#endif

template <typename Event>
static any_event normalize_event(const Event& event)
{
return any_event{event};
}

constexpr static const any_event& normalize_event(const any_event& event)
{
return event;
Expand All @@ -88,50 +94,88 @@ struct compile_policy_impl<favor_compile_time>
return helper.is_end_interrupt_event(event);
}

template <typename Event>
static any_event normalize_event(const Event& event)
// Dispatch table for event deferral checks.
// Converts any_events and calls 'is_event_deferred' on states.
template <typename StateMachine>
class is_event_deferred_dispatch_table
{
return any_event{event};
}
public:
template <typename State>
static bool dispatch(const StateMachine& sm, const State& state,
const any_event& event)
{
static const is_event_deferred_dispatch_table table{state};
auto it = table.m_cells.find(event.type());
if (it != table.m_cells.end())
{
using real_cell =
bool (*)(const StateMachine&, const State&, const any_event&);
auto cell = reinterpret_cast<real_cell>(it->second);
return (*cell)(sm, state, event);
}
return false;
}

template <typename State>
static const std::unordered_set<std::type_index>& get_deferred_event_type_indices()
{
static std::unordered_set<std::type_index> type_indices = []()
private:
template <typename State>
is_event_deferred_dispatch_table(const State&)
{
std::unordered_set<std::type_index> indices;
using deferred_events = to_mp_list_t<typename State::deferred_events>;
using deferred_event_identities =
mp11::mp_transform<mp11::mp_identity, deferred_events>;
mp11::mp_for_each<deferred_event_identities>(
[&indices](auto event_identity)
[this](auto event_identity)
{
using Event = typename decltype(event_identity)::type;
indices.emplace(to_type_index<Event>());
}
);
return indices;
}();
return type_indices;
}
m_cells[to_type_index<Event>()] =
reinterpret_cast<generic_cell>(&convert_and_execute<State, Event>);
});
}

template<typename State, typename Event>
static bool convert_and_execute(const StateMachine& sm, const State& state,
const any_event& event)
{
return state.is_event_deferred(*any_cast<Event>(&event), sm.get_fsm_argument());
}

std::unordered_map<std::type_index, generic_cell> m_cells;
};

template <typename StateMachine>
struct is_event_deferred_visitor
{
template <typename State>
using predicate = has_deferred_events<State>;

is_event_deferred_visitor(const StateMachine& sm, const any_event& event)
: sm(sm), event(event)
{
}

template <typename State>
void operator()(const State& state)
{
using table = is_event_deferred_dispatch_table<StateMachine>;
result |= table::dispatch(sm, state, event);
}

const StateMachine& sm;
const any_event& event;
bool result{false};
};

template <typename StateMachine>
static bool is_event_deferred(const StateMachine& sm, const any_event& event)
{
if constexpr (
!mp11::mp_empty<typename StateMachine::deferring_states>::value)
{
bool result = false;
const std::type_index type_index = event.type();
auto visitor = [&result, type_index](const auto& state)
{
using State = std::decay_t<decltype(state)>;
const auto& set = get_deferred_event_type_indices<State>();
result |= (set.find(type_index) != set.end());
};
using visitor_t = is_event_deferred_visitor<StateMachine>;
visitor_t visitor{sm, event};
sm.template visit_if<visit_mode::active_non_recursive,
has_deferred_events>(visitor);
return result;
visitor_t::template predicate>(visitor);
return visitor.result;
}
else
{
Expand Down Expand Up @@ -320,8 +364,10 @@ struct compile_policy_impl<favor_compile_time>
template<typename Transition>
using get_init_cell_constant = typename get_init_cell_constant_impl<Transition>::type;

using has_internal_transitions = mp11::mp_not<mp11::mp_empty<internal_transition_table<StateMachine>>>;
using has_transitions = mp11::mp_not<mp11::mp_empty<transition_table<StateMachine>>>;
using has_internal_transitions =
mp11::mp_not<mp11::mp_empty<internal_transition_table<StateMachine>>>;
using has_transitions =
mp11::mp_not<mp11::mp_empty<transition_table<StateMachine>>>;

// Dispatch table for one state.
class state_dispatch_table
Expand Down
8 changes: 7 additions & 1 deletion include/boost/msm/backmp11/state_machine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ class state_machine_base : public FrontEnd
derived_t,
typename config_t::root_sm>;

fsm_parameter_t& get_fsm_argument()
const fsm_parameter_t& get_fsm_argument() const
{
if constexpr (std::is_same_v<typename config_t::fsm_parameter,
local_transition_owner>)
Expand All @@ -668,6 +668,12 @@ class state_machine_base : public FrontEnd
}
}

fsm_parameter_t& get_fsm_argument()
{
return const_cast<fsm_parameter_t&>
(static_cast<const state_machine_base&>(*this).get_fsm_argument());
}

// Checks if an event is an end interrupt event.
template <typename Event>
bool is_end_interrupt_event(const Event& event) const
Expand Down
13 changes: 9 additions & 4 deletions include/boost/msm/front/detail/common_states.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,15 @@ struct state_base : public inherit_attributes<Attributes>, USERBASE

// empty implementation for the states not wishing to define an entry condition
// will not be called polymorphic way
template <class Event,class FSM>
void on_entry(Event const& ,FSM&){}
template <class Event,class FSM>
void on_exit(Event const&,FSM& ){}
template <class Event, class FSM>
void on_entry(Event const&, FSM&) {}
template <class Event, class FSM>
void on_exit(Event const&, FSM&) {}
template <class Event, class FSM>
bool is_event_deferred(Event const&, FSM&) const
{
return true;
}
// default (empty) transition table;
typedef ::boost::mpl::vector<> internal_transition_table;
typedef ::boost::fusion::vector<> internal_transition_table11;
Expand Down
6 changes: 6 additions & 0 deletions include/boost/msm/front/puml/puml.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ namespace detail {
}
});
}
// functions added for front::state compatibility
template <class Event, class FSM>
bool is_event_deferred(Event const&, FSM&) const
{
return true;
}
// typedefs added for front::state compatibility
typedef ::boost::mpl::vector<> internal_transition_table;
typedef ::boost::fusion::vector<> internal_transition_table11;
Expand Down
Loading