Skip to content
Closed
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
57 changes: 43 additions & 14 deletions include/boost/msm/backmp11/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,43 @@
# Boost MSM backmp11 backend

The new backend `backmp11` is a new backwards-compatible backend that has the following goals:
This README file is temporary and contains information about `backmp11`, a new backend that is mostly backwards-compatible with `back`. It is currently in **experimental** stage, thus some details about the compatibility might change (feedback welcome!). This file's contents should eventually move into the MSM documentation.

The new backend has the following goals:

- reduce compilation runtime and RAM usage
- reduce state machine runtime

It is named after the metaprogramming library Boost Mp11, the main contributor to the optimizations:
It replaces usages of MPL with Mp11 to get rid of the C++03 emulation of variadic templates.
This backend contains additional optimizations that are further described below.
It is named after the metaprogramming library Boost Mp11, the main contributor to the optimizations. Usages of MPL are replaced with Mp11 to get rid of the costly C++03 emulation of variadic templates.


## New features


## Resolved limitations

### Forwarding constructor arguments to the frontend

The number of max. constructor arguments is not limited anymore to 'BOOST_MSM_CONSTRUCTOR_ARG_SIZE' and references can be passed directly, there is no need to wrap them with 'boost::ref'.


## Breaking changes

### The targeted minimum C++ version is C++17

C++11 brings the strongly needed variadic template support for MSM, but later C++ versions provide other important features - for example C++17's `if constexpr`.


### The eUML frontend is not supported

The support of EUML induces longer compilation times by the need to include the Boost proto headers and applying C++03 variadic template emulation. If you want to use a UML-like syntax, please try out the new PUML frontend.


### The backend's constructor does not allow initialization of states and `set_states` is not available

There were some caveats with one constructor that was used for different use cases: On the one hand some arguments were immediately forwarded to the frontend's constructor, on the other hand the stream operator was used to identify other arguments in the constructor as states, to copy them into the state machine. Besides the syntax of the later being rather unusual, when doing both at once the syntax becomes too difficult to understand; even more so if states within hierarchical sub state machines were initialized in this fashion.

In order to keep API of the constructor simpler and less ambiguous, it only supports forwarding arguments to the frontend and no more.
Also the `set_states` API is removed. If setting a state is required, this can still be done (in a little more verbose, but also more direct & explicit fashion) by getting a reference to the desired state via `get_state` and then assigning the desired new state to it.


## How to use it
Expand All @@ -20,20 +50,20 @@ When using the `favor_compile_time` policy, a different macro to generate missin
- use `BOOST_MSM_BACKMP11_GENERATE_DISPATCH_TABLE(<fsmname>)` in place of `BOOST_MSM_BACK_GENERATE_PROCESS_EVENT(<fsmname>)`


## General optimizations
## Applied optimizations

- Replacement of CPU-intensive calls due to C++03 recursion from MPL to Mp11
- Replacement of CPU-intensive calls (due to C++03 recursion from MPL) with Mp11
- Applied type punning where useful (to reduce template instantiations, e.g. std::deque & other things around the dispatch table)


## Optimizations applied to the `favor_runtime_speed` policy
### `favor_runtime_speed` policy

Summary:
- Optimized cell initialization with initializer arrays (to reduce template instantiations)
- Default-initialized everything and afterwards only defer transition cells
- Default-initialized everything and afterwards only the defer transition cells


## Optimizations applied to the `favor_compile_time` policy
### `favor_compile_time` policy

Once an event is given to the FSM for processing, it is immediately converted to `any` and processing continues with this `any` event.
The structure of the dispatch table has been reworked, one dispatch table is created per state as a hash map.
Expand All @@ -44,14 +74,13 @@ The new mechanism renders the `process_any_event` function obsolete and enables

Summary:
- Use one dispatch table per state to reduce compiler processing time
- The algorithms for procesing the STT and states are optimized to go through rows and states only once
- The algorithms for processing the STT and states are optimized to go through rows and states only once
- These dispatch tables are hash tables with type_id as key
- Apply type erasure with boost::any as early as possible and do further processing only with any events
- Apply type erasure with `any` as early as possible and do further processing only with any events
- each dispatch table only has to cover the events it's handling, no template instantiations required for forwarding events to sub-SMs
- Use `std::any` if C++17 is available (up to 30% runtime impact because of small value optimization in `std::any`)


## Learnings:
### Learnings:

- If only a subset needs to be processed, prefer copy_if & transform over fold
- Selecting a template-based function overload in Mp11 seems more efficient than using enable_if/disable_if
- Selecting a template-based function overload in Mp11 seems more efficient than using enable_if/disable_if
177 changes: 13 additions & 164 deletions include/boost/msm/backmp11/state_machine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,7 @@

#include <boost/msm/active_state_switching_policies.hpp>
#include <boost/msm/row_tags.hpp>
#include <boost/msm/msm_grammar.hpp>
#include <boost/msm/back/traits.hpp>
#include <boost/msm/back/fold_to_list.hpp>
#include <boost/msm/backmp11/favor_compile_time.hpp>
#include <boost/msm/backmp11/metafunctions.hpp>
#include <boost/msm/backmp11/history_policies.hpp>
Expand All @@ -78,7 +76,6 @@ namespace boost { namespace msm { namespace backmp11

using back::no_fsm_check;
using back::queue_container_deque;
using back::FoldToList;


// event used internally for wrapping a direct entry
Expand Down Expand Up @@ -116,17 +113,6 @@ typedef ::boost::parameter::parameters<
>
> state_machine_signature;

// just here to disable use of proto when not needed
template <class T, class F,class Enable=void>
struct make_euml_terminal;
template <class T,class F>
struct make_euml_terminal<T,F,typename ::boost::disable_if<has_using_declared_table<F> >::type>
{};
template <class T,class F>
struct make_euml_terminal<T,F,typename ::boost::enable_if<has_using_declared_table<F> >::type>
: public proto::extends<typename proto::terminal< boost::msm::state_tag>::type, T, boost::msm::state_domain>
{};

// library-containing class for state machines. Pass the actual FSM class as
// the Concrete parameter.
// A0=Derived,A1=NoHistory,A2=CompilePolicy,A3=FsmCheckPolicy >
Expand All @@ -141,11 +127,6 @@ class state_machine : //public Derived
public ::boost::parameter::binding<
typename state_machine_signature::bind<A0,A1,A2,A3,A4>::type, tag::front_end
>::type
, public make_euml_terminal<state_machine<A0,A1,A2,A3,A4>,
typename ::boost::parameter::binding<
typename state_machine_signature::bind<A0,A1,A2,A3,A4>::type, tag::front_end
>::type
>
{
public:
// Create ArgumentPack
Expand Down Expand Up @@ -1654,160 +1635,28 @@ class state_machine : //public Derived
int* const m_initial_states;
int m_index;
};
public:
struct update_state
{
update_state(substate_list& to_overwrite_):to_overwrite(&to_overwrite_){}
template<typename StateType>
void operator()(StateType const& astate) const
{
std::get<get_state_id<stt, StateType>::value>(*to_overwrite)=astate;
}
substate_list* to_overwrite;
};
template <class Expr>
void set_states(Expr const& expr)
{
::boost::fusion::for_each(
::boost::fusion::as_vector(FoldToList()(expr, boost::fusion::nil_())),update_state(this->m_substate_list));
}

// Construct with the default initial states
state_machine()
:Derived()
,m_events_queue()
,m_deferred_events_queue()
,m_history()
,m_event_processing(false)
,m_is_included(false)
,m_visitors()
,m_substate_list()
{
// initialize our list of states with the ones defined in Derived::initial_state
::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> >
(init_states(m_states));
m_history.set_initial_states(m_states);
// create states
fill_states(this);
}
public:

// Construct with the default initial states and some default argument(s)
#if defined (BOOST_NO_CXX11_RVALUE_REFERENCES) \
|| defined (BOOST_NO_CXX11_VARIADIC_TEMPLATES) \
|| defined (BOOST_NO_CXX11_FUNCTION_TEMPLATE_DEFAULT_ARGS)
template <class Expr>
state_machine
(Expr const& expr, typename ::boost::enable_if<typename ::boost::proto::is_expr<Expr>::type >::type* = 0)
:Derived()
, m_events_queue()
, m_deferred_events_queue()
, m_history()
, m_event_processing(false)
, m_is_included(false)
, m_visitors()
, m_substate_list()
// Construct with the default initial states and forward constructor arguments to the frontend.
template <typename... Args>
state_machine(Args&&... args)
: Derived(std::forward<Args>(args)...)
,m_events_queue()
,m_deferred_events_queue()
,m_history()
,m_event_processing(false)
,m_is_included(false)
,m_visitors()
,m_substate_list()
{
BOOST_MPL_ASSERT_MSG(
(::boost::proto::matches<Expr, FoldToList>::value),
THE_STATES_EXPRESSION_PASSED_DOES_NOT_MATCH_GRAMMAR,
(FoldToList));

// initialize our list of states with the ones defined in Derived::initial_state
::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> >
(init_states(m_states));
m_history.set_initial_states(m_states);
// create states
set_states(expr);
fill_states(this);
}
#define MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB(z, n, unused) ARG ## n t ## n
#define MSM_CONSTRUCTOR_HELPER_EXECUTE(z, n, unused) \
template <BOOST_PP_ENUM_PARAMS(n, class ARG)> \
state_machine<A0,A1,A2,A3,A4 \
>(BOOST_PP_ENUM(n, MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB, ~ ), \
typename ::boost::disable_if<typename ::boost::proto::is_expr<ARG0>::type >::type* =0 ) \
:Derived(BOOST_PP_ENUM_PARAMS(n,t)) \
,m_events_queue() \
,m_deferred_events_queue() \
,m_history() \
,m_event_processing(false) \
,m_is_included(false) \
,m_visitors() \
,m_substate_list() \
{ \
::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > \
(init_states(m_states)); \
m_history.set_initial_states(m_states); \
fill_states(this); \
} \
template <class Expr,BOOST_PP_ENUM_PARAMS(n, class ARG)> \
state_machine<A0,A1,A2,A3,A4 \
>(Expr const& expr,BOOST_PP_ENUM(n, MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB, ~ ), \
typename ::boost::enable_if<typename ::boost::proto::is_expr<Expr>::type >::type* =0 ) \
:Derived(BOOST_PP_ENUM_PARAMS(n,t)) \
,m_events_queue() \
,m_deferred_events_queue() \
,m_history() \
,m_event_processing(false) \
,m_is_included(false) \
,m_visitors() \
,m_substate_list() \
{ \
BOOST_MPL_ASSERT_MSG( \
( ::boost::proto::matches<Expr, FoldToList>::value), \
THE_STATES_EXPRESSION_PASSED_DOES_NOT_MATCH_GRAMMAR, \
(FoldToList)); \
::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> > \
(init_states(m_states)); \
m_history.set_initial_states(m_states); \
set_states(expr); \
fill_states(this); \
}

BOOST_PP_REPEAT_FROM_TO(1,BOOST_PP_ADD(BOOST_MSM_CONSTRUCTOR_ARG_SIZE,1), MSM_CONSTRUCTOR_HELPER_EXECUTE, ~)
#undef MSM_CONSTRUCTOR_HELPER_EXECUTE
#undef MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB

#else
template <class ARG0,class... ARG,class=typename ::boost::disable_if<typename ::boost::proto::is_expr<ARG0>::type >::type>
state_machine(ARG0&& t0,ARG&&... t)
:Derived(std::forward<ARG0>(t0), std::forward<ARG>(t)...)
,m_events_queue()
,m_deferred_events_queue()
,m_history()
,m_event_processing(false)
,m_is_included(false)
,m_visitors()
,m_substate_list()
{
::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> >
(init_states(m_states));
m_history.set_initial_states(m_states);
// create states
fill_states(this);
}
template <class Expr,class... ARG,class=typename ::boost::enable_if<typename ::boost::proto::is_expr<Expr>::type >::type>
state_machine(Expr const& expr,ARG&&... t)
:Derived(std::forward<ARG>(t)...)
,m_events_queue()
,m_deferred_events_queue()
,m_history()
,m_event_processing(false)
,m_is_included(false)
,m_visitors()
,m_substate_list()
{
BOOST_MPL_ASSERT_MSG(
( ::boost::proto::matches<Expr, FoldToList>::value),
THE_STATES_EXPRESSION_PASSED_DOES_NOT_MATCH_GRAMMAR,
(FoldToList));
::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap<mpl::placeholders::_1> >
(init_states(m_states));
m_history.set_initial_states(m_states);
set_states(expr);
fill_states(this);
}
#endif


// assignment operator using the copy policy to decide if non_copyable, shallow or deep copying is necessary
library_sm& operator= (library_sm const& rhs)
Expand Down
5 changes: 2 additions & 3 deletions test/AnonymousEuml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
// http://www.boost.org/LICENSE_1_0.txt)

// back-end
// EUML is not supported by backmp11
#define BOOST_MSM_TEST_SKIP_BACKMP11
#include "BackCommon.hpp"
#include <boost/msm/front/euml/euml.hpp>

Expand Down Expand Up @@ -163,6 +165,3 @@ namespace

}
}

using backmp11_fsm = boost::msm::backmp11::state_machine<my_machine_, boost::msm::backmp11::favor_compile_time>;
BOOST_MSM_BACKMP11_GENERATE_DISPATCH_TABLE(backmp11_fsm);
4 changes: 4 additions & 0 deletions test/BackCommon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@ template<typename Front>
using get_test_machines = boost::mpl::vector<
boost::msm::back::state_machine<Front>,
boost::msm::back::state_machine<Front, boost::msm::back::favor_compile_time>,
#if !defined(BOOST_MSM_TEST_SKIP_BACKMP11)
boost::msm::backmp11::state_machine<Front>,
boost::msm::backmp11::state_machine<Front, boost::msm::backmp11::favor_compile_time>,
#endif // BOOST_MSM_TEST_SKIP_BACKMP11
boost::msm::back11::state_machine<Front>
>;

template <template <template <typename...> class, typename = void> class hierarchical>
using get_hierarchical_test_machines = boost::mpl::vector<
hierarchical<boost::msm::back::state_machine>,
hierarchical<boost::msm::back::state_machine, boost::msm::back::favor_compile_time>,
#if !defined(BOOST_MSM_TEST_SKIP_BACKMP11)
hierarchical<boost::msm::backmp11::state_machine>,
hierarchical<boost::msm::backmp11::state_machine, boost::msm::backmp11::favor_compile_time>,
#endif // BOOST_MSM_TEST_SKIP_BACKMP11
hierarchical<boost::msm::back11::state_machine>
>;

Expand Down
Loading
Loading