diff --git a/include/boost/msm/backmp11/README.md b/include/boost/msm/backmp11/README.md index 03ca5146..acf28cca 100644 --- a/include/boost/msm/backmp11/README.md +++ b/include/boost/msm/backmp11/README.md @@ -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 @@ -20,20 +50,20 @@ When using the `favor_compile_time` policy, a different macro to generate missin - use `BOOST_MSM_BACKMP11_GENERATE_DISPATCH_TABLE()` in place of `BOOST_MSM_BACK_GENERATE_PROCESS_EVENT()` -## 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. @@ -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 \ No newline at end of file diff --git a/include/boost/msm/backmp11/state_machine.hpp b/include/boost/msm/backmp11/state_machine.hpp index 132c77dd..3123f26c 100644 --- a/include/boost/msm/backmp11/state_machine.hpp +++ b/include/boost/msm/backmp11/state_machine.hpp @@ -57,9 +57,7 @@ #include #include -#include #include -#include #include #include #include @@ -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 @@ -116,17 +113,6 @@ typedef ::boost::parameter::parameters< > > state_machine_signature; -// just here to disable use of proto when not needed -template -struct make_euml_terminal; -template -struct make_euml_terminal >::type> -{}; -template -struct make_euml_terminal >::type> - : public proto::extends::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 > @@ -141,11 +127,6 @@ class state_machine : //public Derived public ::boost::parameter::binding< typename state_machine_signature::bind::type, tag::front_end >::type - , public make_euml_terminal, - typename ::boost::parameter::binding< - typename state_machine_signature::bind::type, tag::front_end - >::type - > { public: // Create ArgumentPack @@ -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 - void operator()(StateType const& astate) const - { - std::get::value>(*to_overwrite)=astate; - } - substate_list* to_overwrite; - }; - template - 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 > - (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 - state_machine - (Expr const& expr, typename ::boost::enable_if::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 + state_machine(Args&&... args) + : Derived(std::forward(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::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 > - (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 \ - state_machine(BOOST_PP_ENUM(n, MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB, ~ ), \ - typename ::boost::disable_if::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 > \ - (init_states(m_states)); \ - m_history.set_initial_states(m_states); \ - fill_states(this); \ - } \ - template \ - state_machine(Expr const& expr,BOOST_PP_ENUM(n, MSM_CONSTRUCTOR_HELPER_EXECUTE_SUB, ~ ), \ - typename ::boost::enable_if::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::value), \ - THE_STATES_EXPRESSION_PASSED_DOES_NOT_MATCH_GRAMMAR, \ - (FoldToList)); \ - ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap > \ - (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 ::type >::type> - state_machine(ARG0&& t0,ARG&&... t) - :Derived(std::forward(t0), std::forward(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 > (init_states(m_states)); m_history.set_initial_states(m_states); + // create states fill_states(this); } - template ::type >::type> - state_machine(Expr const& expr,ARG&&... t) - :Derived(std::forward(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::value), - THE_STATES_EXPRESSION_PASSED_DOES_NOT_MATCH_GRAMMAR, - (FoldToList)); - ::boost::mpl::for_each< seq_initial_states, ::boost::msm::wrap > - (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) diff --git a/test/AnonymousEuml.cpp b/test/AnonymousEuml.cpp index cca60a74..3d738049 100644 --- a/test/AnonymousEuml.cpp +++ b/test/AnonymousEuml.cpp @@ -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 @@ -163,6 +165,3 @@ namespace } } - -using backmp11_fsm = boost::msm::backmp11::state_machine; -BOOST_MSM_BACKMP11_GENERATE_DISPATCH_TABLE(backmp11_fsm); \ No newline at end of file diff --git a/test/BackCommon.hpp b/test/BackCommon.hpp index 482e4238..621e8411 100644 --- a/test/BackCommon.hpp +++ b/test/BackCommon.hpp @@ -19,8 +19,10 @@ template using get_test_machines = boost::mpl::vector< boost::msm::back::state_machine, boost::msm::back::state_machine, +#if !defined(BOOST_MSM_TEST_SKIP_BACKMP11) boost::msm::backmp11::state_machine, boost::msm::backmp11::state_machine, +#endif // BOOST_MSM_TEST_SKIP_BACKMP11 boost::msm::back11::state_machine >; @@ -28,8 +30,10 @@ template