Skip to content
Open
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
25 changes: 23 additions & 2 deletions doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ a| Deferring an event as an action triggered by the same event is not foreseen i

*Change:* The public API to defer an event will be changed to `protected`. If needed, users can inherit from `state_machine` and make the API public again, but without guarantees about correct functionality and API consistency.

*Recommendation:* Configure event deferral as a state property instead of using it as a transition action.
*Recommendation:* Configure event deferral as a state property instead of using it as a transition action. Implement conditional deferral with the state's `is_event_deferred` method if needed.

| Public access to the event container
| 1.90 / 1.91
Expand Down Expand Up @@ -259,6 +259,27 @@ A similar friend declaration is available in the `history_impl` classes.
**IMPORTANT:** This design allows you to provide any serializer implementation, but due to the need to access private members there is no guarantee that your implementation breaks in a new version of the back-end.


=== 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 @@ -400,7 +421,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
5 changes: 5 additions & 0 deletions doc/modules/ROOT/pages/version-history.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

= Version history

== Boost 1.91

* feat/backmp11: Applied further optimizations to achieve ~20% compile time & memory usage reduction for the `favor_runtime_speed` policy compared to the Boost 1.90 release
* feat/backmp11: Support for conditional event deferral with the `deferred_events` property

== Boost 1.90

* feat/backmp11: New back-end backmp11, requires C++ 17
Expand Down
10 changes: 5 additions & 5 deletions include/boost/msm/backmp11/detail/favor_runtime_speed.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,12 @@ struct compile_policy_impl<favor_runtime_speed>
class is_event_deferred_helper
{
public:
static bool execute(const StateMachine& sm)
static bool execute(const StateMachine& sm, const Event& event)
{
bool result = false;
auto visitor = [&result](const auto& /*state*/)
auto visitor = [&result, &sm, &event](const auto& state)
{
result = true;
result |= state.is_event_deferred(event, sm);
};
// Apply a pre-filter with the 'has_deferred_events' predicate,
// since this subset needs to be instantiated only once for the SM.
Expand All @@ -107,10 +107,10 @@ struct compile_policy_impl<favor_runtime_speed>
};

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)
{
using helper = is_event_deferred_helper<StateMachine, Event>;
return helper::execute(sm);
return helper::execute(sm, event);
}

template <typename StateMachine, typename Event>
Expand Down
61 changes: 44 additions & 17 deletions include/boost/msm/backmp11/favor_compile_time.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,36 +108,63 @@ struct compile_policy_impl<favor_compile_time>
template <typename StateOrStates, typename Event>
using has_deferred_event = has_deferred_events<StateOrStates>;

template <typename State>
static const std::unordered_set<std::type_index>& get_deferred_event_type_indices()
// Helper class to check whether a state conditionally defers an event.
template <typename StateMachine>
class is_event_deferred_dispatch_table
{
static std::unordered_set<std::type_index> type_indices = []()
public:
template <typename State>
static bool dispatch(const StateMachine& sm, const State& state, const any_event& event)
{
const auto& table = is_event_deferred_dispatch_table::instance(mp11::mp_identity<State>{});
auto it = table.m_entries.find(event.type());
if (it != table.m_entries.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;
}

private:
template <typename State>
is_event_deferred_dispatch_table(mp11::mp_identity<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_entries[to_type_index<Event>()] =
reinterpret_cast<generic_cell>(&convert_and_execute<Event, State>);
});
}

template<typename Event, typename State>
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);
}

template <typename State>
static const is_event_deferred_dispatch_table& instance(mp11::mp_identity<State> state_identity)
{
static const is_event_deferred_dispatch_table table{state_identity};
return table;
}

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

template <typename StateMachine>
static bool is_event_deferred(const StateMachine& sm, const any_event& event)
{
bool result = false;
const std::type_index type_index = event.type();
auto visitor = [&result, type_index](const auto& state)
auto visitor = [&result, &sm, &event](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());
result |= is_event_deferred_dispatch_table<StateMachine>::dispatch(sm, state, event);
};
sm.template visit_if<visit_mode::active_non_recursive,
has_deferred_events>(visitor);
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