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
19 changes: 10 additions & 9 deletions doc/modules/ROOT/pages/tutorial/backmp11-back-end.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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<typename T, typename A0, typename A1, typename A2>
friend void serialize(T&, state_machine<A0, A1, A2>&);
void serialize(T&, state_machine<A0, A1, A2>&);
```

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

Expand Down Expand Up @@ -471,6 +465,13 @@ class state_machine_adapter
this->get_event_pool().events.clear();
}

template <class Archive>
void serialize(Archive& ar,
const unsigned int /*version*/)
{
msm::backmp11::serialize(ar, *this);
}

// No adapter.
// Superseded by the visitor API.
// void visit_current_states(...) {...}
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.92

* New features (`backmp11`):
** Improve serializer interface (https://github.com/boostorg/msm/issues/197[#197])

== Boost 1.91

* New features (`backmp11`):
Expand Down
31 changes: 19 additions & 12 deletions include/boost/msm/backmp11/detail/history_impl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,13 @@ class history_impl<front::no_history, NumberOfRegions>
return true;
}

private:
// Allow access to private members for serialization.
template<typename T, int N>
friend void serialize(T&, history_impl<front::no_history, N>&);
template<typename T>
void serialize(T& archive)
{
archive & m_initial_state_ids;
}

private:
std::array<int, NumberOfRegions> m_initial_state_ids;
};

Expand Down Expand Up @@ -83,11 +85,13 @@ class history_impl<front::always_shallow_history, NumberOfRegions>
return false;
}

private:
// Allow access to private members for serialization.
template<typename T, int N>
friend void serialize(T&, history_impl<front::always_shallow_history, N>&);
template<typename T>
void serialize(T& archive)
{
archive & m_last_active_state_ids;
}

private:
std::array<int, NumberOfRegions> m_last_active_state_ids;
};

Expand Down Expand Up @@ -125,11 +129,14 @@ class history_impl<front::shallow_history<Events...>, NumberOfRegions>
return !mp11::mp_contains<events_mp11,Event>::value;
}

private:
// Allow access to private members for serialization.
template<typename T, typename... Es, int N>
friend void serialize(T&, history_impl<front::shallow_history<Es...>, N>&);
template<typename T>
void serialize(T& archive)
{
archive & m_initial_state_ids;
archive & m_last_active_state_ids;
}

private:
std::array<int, NumberOfRegions> m_initial_state_ids;
std::array<int, NumberOfRegions> m_last_active_state_ids;
};
Expand Down
34 changes: 31 additions & 3 deletions include/boost/msm/backmp11/detail/state_machine_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<typename T, typename A0, typename A1, typename A2>
friend void serialize(T&, state_machine_base<A0, A1, A2>&);

Expand Down Expand Up @@ -1144,6 +1141,37 @@ class state_machine_base : public FrontEnd
bool m_running{false};
};

template <typename T, typename Event, typename = void>
struct has_serialize : std::false_type {};

template <typename T, typename State>
struct has_serialize<T, State,
std::void_t<decltype(std::declval<State&>().serialize(std::declval<T&>()))>>
: std::true_type {};

template<typename T, typename A0, typename A1, typename A2>
void serialize(T& archive, state_machine_base<A0, A1, A2>& sm)
{
using FrontEnd = typename state_machine_base<A0, A1, A2>::front_end_t;
if constexpr (has_serialize<T, FrontEnd>::value)
{
static_cast<FrontEnd&>(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<decltype(state)>;
if constexpr (has_serialize<T, State>::value ||
has_state_machine_tag<State>::value)
{
archive & state;
}
});
}

} // boost::msm::backmp11::detail

#endif // BOOST_MSM_BACKMP11_DETAIL_STATE_MACHINE_BASE_HPP
6 changes: 6 additions & 0 deletions include/boost/msm/backmp11/state_machine.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ class state_machine<FrontEnd>
using Base::Base;
};

template<typename T, typename A0, typename A1, typename A2>
void serialize(T& archive, detail::state_machine_base<A0, A1, A2>& sm)
{
detail::serialize(archive, sm);
}

} // boost::msm::backmp11

#endif // BOOST_MSM_BACKMP11_STATE_MACHINE_HPP
98 changes: 7 additions & 91 deletions test/Backmp11Adapter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
#include <boost/msm/back/queue_container_deque.hpp>
#define BOOST_PARAMETER_CAN_USE_MP11
#include <boost/parameter.hpp>
#include <boost/serialization/base_object.hpp>
#include <boost/serialization/array.hpp>
#include "Backmp11.hpp"

Expand Down Expand Up @@ -123,6 +122,13 @@ class state_machine_adapter
this->get_event_pool().events.clear();
}

template <class Archive>
void serialize(Archive& ar,
const unsigned int /*version*/)
{
msm::backmp11::serialize(ar, *this);
}

// No adapter.
// Superseded by the visitor API.
// void visit_current_states(...) {...}
Expand All @@ -136,94 +142,4 @@ class state_machine_adapter
// auto get_state_by_id(int id) {...}
};

template <class Archive>
struct serialize_state
{
serialize_state(Archive& ar):ar_(ar){}

template<typename T>
typename ::boost::enable_if<
typename ::boost::mpl::or_<
typename has_do_serialize<T>::type,
typename detail::has_state_machine_tag<typename T::internal>::type
>::type
,void
>::type
operator()(T& t) const
{
ar_ & t;
}
template<typename T>
typename ::boost::disable_if<
typename ::boost::mpl::or_<
typename has_do_serialize<T>::type,
typename detail::has_state_machine_tag<typename T::internal>::type
>::type
,void
>::type
operator()(T&) const
{
// no state to serialize
}
Archive& ar_;
};

namespace detail
{

template <class Archive, class FrontEnd, class Config, class Derived>
void serialize(Archive& ar,
state_machine_base<FrontEnd, Config, Derived>& sm)
{
(serialize_state<Archive>(ar))(boost::serialization::base_object<FrontEnd>(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<Archive>(ar));
}

template<typename T, int N>
void serialize(T& ar, detail::history_impl<front::no_history, N>& history)
{
ar & history.m_initial_state_ids;
}

template<typename T, typename... Es, int N>
void serialize(T& ar, detail::history_impl<front::shallow_history<Es...>, N>& history)
{
ar & history.m_initial_state_ids;
ar & history.m_last_active_state_ids;
}

}

} // boost::msm::backmp11

namespace boost::serialization
{

template <class Archive, class A0, class A1, class A2, class A3, class A4>
void serialize(Archive& ar,
msm::backmp11::state_machine_adapter<A0, A1, A2, A3, A4>& sm,
const unsigned int /*version*/)
{
msm::backmp11::detail::serialize(ar, sm);
}

template<typename T, int N>
void serialize(T& ar,
msm::backmp11::detail::history_impl<msm::front::no_history, N>& history,
const unsigned int /*version*/)
{
msm::backmp11::detail::serialize(ar, history);
}

template<typename T, typename... Es, int N>
void serialize(T& ar,
msm::backmp11::detail::history_impl<msm::front::shallow_history<Es...>, N>& history,
const unsigned int /*version*/)
{
msm::backmp11::detail::serialize(ar, history);
}

} // boost::serialization
9 changes: 4 additions & 5 deletions test/Serialize.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ namespace
// to achieve this, ask for it
typedef int do_serialize;
// and provide a serialize
template<class Archive>
void serialize(Archive & ar, const unsigned int )
template <class Archive>
void serialize(Archive& ar, const unsigned int = 0)
{
ar & front_end_data;
}
Expand All @@ -87,12 +87,11 @@ namespace
int some_dummy_data;
// we want Empty to be serialized
typedef int do_serialize;
template<class Archive>
void serialize(Archive & ar, const unsigned int )
template <class Archive>
void serialize(Archive& ar, const unsigned int = 0)
{
ar & some_dummy_data;
}

};
struct Open : public msm::front::state<>
{
Expand Down
Loading