diff --git a/doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc b/doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc index 007193eb..8f729712 100644 --- a/doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc +++ b/doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc @@ -303,20 +303,14 @@ If the `fsm_parameter` is set to `root_sm`, then also the `root_sm` must be set. === Generic support for serializers -The `state_machine` allows access to its private members for serialization purposes with a friend: +The `backmp11` namespace contains a `serialize` function for serialization purposes: ```cpp -// Class boost::msm::backmp11::state_machine template -friend void serialize(T&, state_machine&); +void serialize(T&, state_machine&); ``` -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. -==== +Serializers passed to this function need to provide `operator&` overloads for serialization and deserialization. Boost.Serialization supports such overloads out-of-the-box, an example integration of it is shown in the state machine adapter. === Unified event pool for queued and deferred events @@ -471,6 +465,13 @@ class state_machine_adapter this->get_event_pool().events.clear(); } + template + void serialize(Archive& ar, + const unsigned int /*version*/) + { + msm::backmp11::serialize(ar, *this); + } + // No adapter. // Superseded by the visitor API. // void visit_current_states(...) {...} diff --git a/doc/modules/ROOT/pages/version-history.adoc b/doc/modules/ROOT/pages/version-history.adoc index 77451176..d86ff29d 100644 --- a/doc/modules/ROOT/pages/version-history.adoc +++ b/doc/modules/ROOT/pages/version-history.adoc @@ -2,6 +2,11 @@ = Version history +== Boost 1.92 + +* New features (`backmp11`): +** Improve serializer interface (https://github.com/boostorg/msm/issues/197[#197]) + == Boost 1.91 * New features (`backmp11`): diff --git a/include/boost/msm/backmp11/detail/history_impl.hpp b/include/boost/msm/backmp11/detail/history_impl.hpp index 2794fc1b..0fdb107a 100644 --- a/include/boost/msm/backmp11/detail/history_impl.hpp +++ b/include/boost/msm/backmp11/detail/history_impl.hpp @@ -48,11 +48,13 @@ class history_impl return true; } - private: - // Allow access to private members for serialization. - template - friend void serialize(T&, history_impl&); + template + void serialize(T& archive) + { + archive & m_initial_state_ids; + } + private: std::array m_initial_state_ids; }; @@ -83,11 +85,13 @@ class history_impl return false; } -private: - // Allow access to private members for serialization. - template - friend void serialize(T&, history_impl&); + template + void serialize(T& archive) + { + archive & m_last_active_state_ids; + } +private: std::array m_last_active_state_ids; }; @@ -125,11 +129,14 @@ class history_impl, NumberOfRegions> return !mp11::mp_contains::value; } - private: - // Allow access to private members for serialization. - template - friend void serialize(T&, history_impl, N>&); + template + void serialize(T& archive) + { + archive & m_initial_state_ids; + archive & m_last_active_state_ids; + } + private: std::array m_initial_state_ids; std::array m_last_active_state_ids; }; diff --git a/include/boost/msm/backmp11/detail/state_machine_base.hpp b/include/boost/msm/backmp11/detail/state_machine_base.hpp index 452d8884..0e38b645 100644 --- a/include/boost/msm/backmp11/detail/state_machine_base.hpp +++ b/include/boost/msm/backmp11/detail/state_machine_base.hpp @@ -214,9 +214,6 @@ class state_machine_base : public FrontEnd friend class deferred_event; // Allow access to private members for serialization. - // WARNING: - // No guarantee is given on the private member layout. - // Future changes may break existing serializer implementations. template friend void serialize(T&, state_machine_base&); @@ -1144,6 +1141,37 @@ class state_machine_base : public FrontEnd bool m_running{false}; }; +template +struct has_serialize : std::false_type {}; + +template +struct has_serialize().serialize(std::declval()))>> + : std::true_type {}; + +template +void serialize(T& archive, state_machine_base& sm) +{ + using FrontEnd = typename state_machine_base::front_end_t; + if constexpr (has_serialize::value) + { + static_cast(sm).serialize(archive); + } + archive & sm.m_active_state_ids; + sm.m_history.serialize(archive); + archive & sm.m_event_processing; + mp11::tuple_for_each(sm.m_states, + [&archive](auto& state) + { + using State = std::decay_t; + if constexpr (has_serialize::value || + has_state_machine_tag::value) + { + archive & state; + } + }); +} + } // boost::msm::backmp11::detail #endif // BOOST_MSM_BACKMP11_DETAIL_STATE_MACHINE_BASE_HPP diff --git a/include/boost/msm/backmp11/state_machine.hpp b/include/boost/msm/backmp11/state_machine.hpp index b50395b4..e641fd89 100644 --- a/include/boost/msm/backmp11/state_machine.hpp +++ b/include/boost/msm/backmp11/state_machine.hpp @@ -56,6 +56,12 @@ class state_machine using Base::Base; }; +template +void serialize(T& archive, detail::state_machine_base& sm) +{ + detail::serialize(archive, sm); +} + } // boost::msm::backmp11 #endif // BOOST_MSM_BACKMP11_STATE_MACHINE_HPP diff --git a/test/Backmp11Adapter.hpp b/test/Backmp11Adapter.hpp index ae3e8f01..9f116c22 100644 --- a/test/Backmp11Adapter.hpp +++ b/test/Backmp11Adapter.hpp @@ -16,7 +16,6 @@ #include #define BOOST_PARAMETER_CAN_USE_MP11 #include -#include #include #include "Backmp11.hpp" @@ -123,6 +122,13 @@ class state_machine_adapter this->get_event_pool().events.clear(); } + template + void serialize(Archive& ar, + const unsigned int /*version*/) + { + msm::backmp11::serialize(ar, *this); + } + // No adapter. // Superseded by the visitor API. // void visit_current_states(...) {...} @@ -136,94 +142,4 @@ class state_machine_adapter // auto get_state_by_id(int id) {...} }; -template -struct serialize_state -{ - serialize_state(Archive& ar):ar_(ar){} - - template - typename ::boost::enable_if< - typename ::boost::mpl::or_< - typename has_do_serialize::type, - typename detail::has_state_machine_tag::type - >::type - ,void - >::type - operator()(T& t) const - { - ar_ & t; - } - template - typename ::boost::disable_if< - typename ::boost::mpl::or_< - typename has_do_serialize::type, - typename detail::has_state_machine_tag::type - >::type - ,void - >::type - operator()(T&) const - { - // no state to serialize - } - Archive& ar_; -}; - -namespace detail -{ - -template -void serialize(Archive& ar, - state_machine_base& sm) -{ - (serialize_state(ar))(boost::serialization::base_object(sm)); - ar & sm.m_active_state_ids; - ar & sm.m_history; - ar & sm.m_event_processing; - mp11::tuple_for_each(sm.m_states, serialize_state(ar)); -} - -template -void serialize(T& ar, detail::history_impl& history) -{ - ar & history.m_initial_state_ids; -} - -template -void serialize(T& ar, detail::history_impl, N>& history) -{ - ar & history.m_initial_state_ids; - ar & history.m_last_active_state_ids; -} - -} - } // boost::msm::backmp11 - -namespace boost::serialization -{ - -template -void serialize(Archive& ar, - msm::backmp11::state_machine_adapter& sm, - const unsigned int /*version*/) -{ - msm::backmp11::detail::serialize(ar, sm); -} - -template -void serialize(T& ar, - msm::backmp11::detail::history_impl& history, - const unsigned int /*version*/) -{ - msm::backmp11::detail::serialize(ar, history); -} - -template -void serialize(T& ar, - msm::backmp11::detail::history_impl, N>& history, - const unsigned int /*version*/) -{ - msm::backmp11::detail::serialize(ar, history); -} - -} // boost::serialization diff --git a/test/Serialize.cpp b/test/Serialize.cpp index cd1addfe..7eecc91c 100644 --- a/test/Serialize.cpp +++ b/test/Serialize.cpp @@ -69,8 +69,8 @@ namespace // to achieve this, ask for it typedef int do_serialize; // and provide a serialize - template - void serialize(Archive & ar, const unsigned int ) + template + void serialize(Archive& ar, const unsigned int = 0) { ar & front_end_data; } @@ -87,12 +87,11 @@ namespace int some_dummy_data; // we want Empty to be serialized typedef int do_serialize; - template - void serialize(Archive & ar, const unsigned int ) + template + void serialize(Archive& ar, const unsigned int = 0) { ar & some_dummy_data; } - }; struct Open : public msm::front::state<> {