mirror of
https://github.com/boostorg/msm.git
synced 2026-07-21 13:23:45 +00:00
refactor(backmp11): history handling
This commit is contained in:
committed by
Christian Granzin
parent
d09d4584e4
commit
5bd6700f9b
@@ -12,126 +12,133 @@
|
||||
#ifndef BOOST_MSM_BACKMP11_DETAIL_HISTORY_IMPL_HPP
|
||||
#define BOOST_MSM_BACKMP11_DETAIL_HISTORY_IMPL_HPP
|
||||
|
||||
#include <boost/msm/backmp11/detail/metafunctions.hpp>
|
||||
#include <boost/msm/front/history_policies.hpp>
|
||||
#include <boost/mp11.hpp>
|
||||
|
||||
namespace boost::msm::backmp11::detail
|
||||
{
|
||||
|
||||
template<typename History, int NumberOfRegions>
|
||||
template <typename History, typename InitialStateIds>
|
||||
class history_impl;
|
||||
|
||||
template <int NumberOfRegions>
|
||||
class history_impl<front::no_history, NumberOfRegions>
|
||||
template <typename InitialStateIds>
|
||||
class history_impl<front::no_history, InitialStateIds>
|
||||
{
|
||||
public:
|
||||
void reset_active_state_ids(const std::array<int, NumberOfRegions>& initial_state_ids)
|
||||
template <typename StateMachine, typename Event>
|
||||
void on_entry(StateMachine& sm, const Event&)
|
||||
{
|
||||
m_initial_state_ids = initial_state_ids;
|
||||
}
|
||||
|
||||
template <class Event>
|
||||
const std::array<int, NumberOfRegions>& on_entry(Event const&)
|
||||
{
|
||||
return m_initial_state_ids;
|
||||
}
|
||||
|
||||
void on_exit(const std::array<int, NumberOfRegions>&)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
|
||||
// this policy deletes all waiting deferred events
|
||||
template <class Event>
|
||||
bool clear_event_pool(Event const&) const
|
||||
{
|
||||
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>&);
|
||||
|
||||
std::array<int, NumberOfRegions> m_initial_state_ids;
|
||||
};
|
||||
|
||||
template <int NumberOfRegions>
|
||||
class history_impl<front::always_shallow_history, NumberOfRegions>
|
||||
{
|
||||
public:
|
||||
void reset_active_state_ids(const std::array<int, NumberOfRegions>& initial_state_ids)
|
||||
{
|
||||
m_last_active_state_ids = initial_state_ids;
|
||||
}
|
||||
|
||||
template <class Event>
|
||||
const std::array<int, NumberOfRegions>& on_entry(Event const& )
|
||||
{
|
||||
return m_last_active_state_ids;
|
||||
}
|
||||
|
||||
void on_exit(const std::array<int, NumberOfRegions>& active_state_ids)
|
||||
{
|
||||
m_last_active_state_ids = active_state_ids;
|
||||
}
|
||||
|
||||
// the history policy keeps all deferred events until next reentry
|
||||
template <class Event>
|
||||
bool clear_event_pool(Event const&)const
|
||||
{
|
||||
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>&);
|
||||
|
||||
std::array<int, NumberOfRegions> m_last_active_state_ids;
|
||||
};
|
||||
|
||||
template <typename... Events, int NumberOfRegions>
|
||||
class history_impl<front::shallow_history<Events...>, NumberOfRegions>
|
||||
{
|
||||
using events_mp11 = mp11::mp_list<Events...>;
|
||||
|
||||
public:
|
||||
void reset_active_state_ids(const std::array<int, NumberOfRegions>& initial_state_ids)
|
||||
{
|
||||
m_initial_state_ids = initial_state_ids;
|
||||
m_last_active_state_ids = initial_state_ids;
|
||||
}
|
||||
|
||||
template <class Event>
|
||||
const std::array<int, NumberOfRegions>& on_entry(Event const&)
|
||||
{
|
||||
if constexpr (mp11::mp_contains<events_mp11,Event>::value)
|
||||
sm.m_active_state_ids = value_array<InitialStateIds>;
|
||||
if constexpr (StateMachine::event_pool_member::value)
|
||||
{
|
||||
return m_last_active_state_ids;
|
||||
sm.get_event_pool().events.clear();
|
||||
}
|
||||
return m_initial_state_ids;
|
||||
}
|
||||
|
||||
void on_exit(const std::array<int, NumberOfRegions>& active_state_ids)
|
||||
template <typename StateMachine, typename Event, typename Visitor>
|
||||
void on_entry(StateMachine& sm, const Event& event, Visitor&& visitor)
|
||||
{
|
||||
m_last_active_state_ids = active_state_ids;
|
||||
// First set all active state ids...
|
||||
on_entry(sm, event);
|
||||
// ... then execute each state entry.
|
||||
mp11::mp_for_each<InitialStateIds>(
|
||||
[&sm, &visitor](auto state_id)
|
||||
{
|
||||
auto& state = std::get<decltype(state_id)::value>(sm.m_states);
|
||||
visitor(state);
|
||||
});
|
||||
}
|
||||
|
||||
// the history policy keeps deferred events until next reentry if coming from our history event
|
||||
template <class Event>
|
||||
bool clear_event_pool(Event const&) const
|
||||
template <typename StateMachine>
|
||||
void on_exit(StateMachine&)
|
||||
{
|
||||
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, typename U>
|
||||
friend void serialize(T&, history_impl<front::no_history, U>&);
|
||||
};
|
||||
|
||||
std::array<int, NumberOfRegions> m_initial_state_ids;
|
||||
std::array<int, NumberOfRegions> m_last_active_state_ids;
|
||||
template <typename InitialStateIds>
|
||||
class history_impl<front::always_shallow_history, InitialStateIds>
|
||||
{
|
||||
public:
|
||||
template <typename StateMachine, typename Event>
|
||||
void on_entry(StateMachine& sm, const Event&)
|
||||
{
|
||||
sm.m_active_state_ids = m_last_active_state_ids;
|
||||
}
|
||||
|
||||
template <typename StateMachine, typename Event, typename Visitor>
|
||||
void on_entry(StateMachine& sm, const Event& event, Visitor&& visitor)
|
||||
{
|
||||
// First set all active state ids...
|
||||
on_entry(sm, event);
|
||||
// ... then execute each state entry.
|
||||
sm.template visit<visit_mode::active_non_recursive>(visitor);
|
||||
}
|
||||
|
||||
template <typename StateMachine>
|
||||
void on_exit(StateMachine& sm)
|
||||
{
|
||||
m_last_active_state_ids = sm.m_active_state_ids;
|
||||
}
|
||||
|
||||
private:
|
||||
// Allow access to private members for serialization.
|
||||
template<typename T, typename U>
|
||||
friend void serialize(T&, history_impl<front::always_shallow_history, U>&);
|
||||
|
||||
std::array<int, mp11::mp_size<InitialStateIds>::value>
|
||||
m_last_active_state_ids{value_array<InitialStateIds>};
|
||||
};
|
||||
|
||||
template <typename... Events, typename InitialStateIds>
|
||||
class history_impl<front::shallow_history<Events...>, InitialStateIds>
|
||||
{
|
||||
using events = mp11::mp_list<Events...>;
|
||||
|
||||
public:
|
||||
template <typename StateMachine, typename Event>
|
||||
void on_entry(StateMachine& sm, const Event&)
|
||||
{
|
||||
if constexpr (mp11::mp_contains<events, Event>::value)
|
||||
{
|
||||
sm.m_active_state_ids = m_last_active_state_ids;
|
||||
}
|
||||
else
|
||||
{
|
||||
sm.m_active_state_ids = value_array<InitialStateIds>;
|
||||
if constexpr (StateMachine::event_pool_member::value)
|
||||
{
|
||||
sm.get_event_pool().events.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename StateMachine, typename Event, typename Visitor>
|
||||
void on_entry(StateMachine& sm, const Event& event, Visitor&& visitor)
|
||||
{
|
||||
// First set all active state ids...
|
||||
on_entry(sm, event);
|
||||
// ... then execute each state entry.
|
||||
sm.template visit<visit_mode::active_non_recursive>(visitor);
|
||||
}
|
||||
|
||||
template <typename StateMachine>
|
||||
void on_exit(StateMachine& sm)
|
||||
{
|
||||
m_last_active_state_ids = sm.m_active_state_ids;
|
||||
}
|
||||
|
||||
private:
|
||||
// Allow access to private members for serialization.
|
||||
template<typename T, typename... Es, typename U>
|
||||
friend void serialize(T&, history_impl<front::shallow_history<Es...>, U>&);
|
||||
|
||||
std::array<int, mp11::mp_size<InitialStateIds>::value>
|
||||
m_last_active_state_ids{value_array<InitialStateIds>};
|
||||
};
|
||||
|
||||
} // boost::msm::backmp11
|
||||
|
||||
@@ -100,7 +100,7 @@ struct value_array_impl<mp11::mp_list<Ts...>>
|
||||
{
|
||||
using value_type =
|
||||
typename mp11::mp_front<mp11::mp_list<Ts...>>::value_type;
|
||||
static constexpr value_type value[sizeof...(Ts)] {Ts::value...};
|
||||
static constexpr std::array<value_type, sizeof...(Ts)> value{Ts::value...};
|
||||
};
|
||||
template <typename List>
|
||||
static constexpr const auto& value_array = value_array_impl<List>::value;
|
||||
@@ -269,7 +269,8 @@ using generate_state_set = typename generate_state_set_impl<StateMachine>::type;
|
||||
template <class StateSet>
|
||||
struct generate_state_map_impl
|
||||
{
|
||||
using indices = mp11::mp_iota<mp11::mp_size<StateSet>>;
|
||||
using indices = mp11::mp_iota<
|
||||
std::integral_constant<int, mp11::mp_size<StateSet>::value>>;
|
||||
using type = mp11::mp_transform_q<
|
||||
mp11::mp_bind<mp11::mp_list, mp11::_1, mp11::_2>,
|
||||
StateSet,
|
||||
|
||||
@@ -180,6 +180,11 @@ class state_machine_base : public FrontEnd
|
||||
static constexpr int nr_regions = mp11::mp_size<initial_states>::value;
|
||||
|
||||
using state_set = generate_state_set<state_machine_base>;
|
||||
using state_map = generate_state_map<state_set>;
|
||||
template <typename State>
|
||||
using get_state_id =
|
||||
detail::get_state_id<state_map, State>;
|
||||
|
||||
using submachines = mp11::mp_copy_if<state_set, is_composite>;
|
||||
};
|
||||
|
||||
@@ -217,9 +222,12 @@ class state_machine_base : public FrontEnd
|
||||
|
||||
private:
|
||||
using state_set = typename internal::state_set;
|
||||
using state_map = typename internal::state_map;
|
||||
static constexpr int nr_regions = internal::nr_regions;
|
||||
using active_state_ids_t = std::array<int, nr_regions>;
|
||||
using initial_state_identities = mp11::mp_transform<mp11::mp_identity, typename internal::initial_states>;
|
||||
using initial_state_ids =
|
||||
mp11::mp_transform<internal::template get_state_id,
|
||||
typename internal::initial_states>;
|
||||
using compile_policy = typename config_t::compile_policy;
|
||||
using compile_policy_impl = detail::compile_policy_impl<compile_policy>;
|
||||
|
||||
@@ -232,6 +240,9 @@ class state_machine_base : public FrontEnd
|
||||
template <class, class, class>
|
||||
friend class state_machine_base;
|
||||
|
||||
template <typename, typename>
|
||||
friend class history_impl;
|
||||
|
||||
template <typename StateMachine>
|
||||
friend struct transition_table_impl;
|
||||
|
||||
@@ -268,8 +279,8 @@ class state_machine_base : public FrontEnd
|
||||
using fsm_final_event =
|
||||
mp11::mp_eval_or<stopping, get_final_event, front_end_t>;
|
||||
|
||||
using state_map = generate_state_map<state_set>;
|
||||
using history_impl = detail::history_impl<typename front_end_t::history, nr_regions>;
|
||||
using history_impl = detail::history_impl<typename front_end_t::history,
|
||||
initial_state_ids>;
|
||||
|
||||
using context_member =
|
||||
optional_instance<context_t*,
|
||||
@@ -319,7 +330,7 @@ class state_machine_base : public FrontEnd
|
||||
visit_if<visit_mode::all_recursive,
|
||||
visitor_t::template predicate>(visitor);
|
||||
}
|
||||
reset_active_state_ids();
|
||||
m_active_state_ids = value_array<initial_state_ids>;
|
||||
}
|
||||
|
||||
// Construct with a context and
|
||||
@@ -660,19 +671,6 @@ class state_machine_base : public FrontEnd
|
||||
return compile_policy_impl::is_end_interrupt_event(*this, event);
|
||||
}
|
||||
|
||||
// Helpers used to reset the state machine.
|
||||
void reset_active_state_ids()
|
||||
{
|
||||
size_t index = 0;
|
||||
mp11::mp_for_each<initial_state_identities>(
|
||||
[this, &index](auto state_identity)
|
||||
{
|
||||
using State = typename decltype(state_identity)::type;
|
||||
m_active_state_ids[index++] = get_state_id<State>();
|
||||
});
|
||||
m_history.reset_active_state_ids(m_active_state_ids);
|
||||
}
|
||||
|
||||
// Main function used internally to process events.
|
||||
// Explicitly not inline, because code size can significantly increase if
|
||||
// this method is inlined in all existing process_info variants.
|
||||
@@ -1008,25 +1006,8 @@ class state_machine_base : public FrontEnd
|
||||
{
|
||||
preprocess_entry(event, fsm);
|
||||
|
||||
// First set all active state ids...
|
||||
m_active_state_ids = m_history.on_entry(event);
|
||||
// ... then execute each state entry.
|
||||
state_entry_visitor<Event> visitor{self(), event};
|
||||
if constexpr (std::is_same_v<typename front_end_t::history,
|
||||
front::no_history>)
|
||||
{
|
||||
mp11::mp_for_each<initial_state_identities>(
|
||||
[this, &visitor](auto state_identity)
|
||||
{
|
||||
using State = typename decltype(state_identity)::type;
|
||||
auto& state = this->get_state<State>();
|
||||
visitor(state);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
visit<visit_mode::active_non_recursive>(visitor);
|
||||
}
|
||||
m_history.on_entry(self(), event, visitor);
|
||||
|
||||
postprocess_entry();
|
||||
}
|
||||
@@ -1044,7 +1025,7 @@ class state_machine_base : public FrontEnd
|
||||
// First set all active state ids...
|
||||
if constexpr (!all_regions_defined)
|
||||
{
|
||||
m_active_state_ids = m_history.on_entry(event);
|
||||
m_history.on_entry(self(), event);
|
||||
}
|
||||
mp11::mp_for_each<state_identities>(
|
||||
[this](auto state_identity)
|
||||
@@ -1116,15 +1097,7 @@ class state_machine_base : public FrontEnd
|
||||
// Then call our own exit.
|
||||
(static_cast<front_end_t*>(this))->on_exit(event, fsm);
|
||||
// Give the history a chance to handle this (or not).
|
||||
m_history.on_exit(this->m_active_state_ids);
|
||||
// History decides what happens with the event pool.
|
||||
if (m_history.clear_event_pool(event))
|
||||
{
|
||||
if constexpr (event_pool_member::value)
|
||||
{
|
||||
get_event_pool().events.clear();
|
||||
}
|
||||
}
|
||||
m_history.on_exit(self());
|
||||
}
|
||||
|
||||
derived_t& self()
|
||||
|
||||
@@ -182,16 +182,14 @@ void serialize(Archive& ar,
|
||||
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)
|
||||
template<typename T, typename InitialStateIds>
|
||||
void serialize(T&, detail::history_impl<front::no_history, InitialStateIds>&)
|
||||
{
|
||||
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)
|
||||
template<typename T, typename... Es, typename InitialStateIds>
|
||||
void serialize(T& ar, detail::history_impl<front::shallow_history<Es...>, InitialStateIds>& history)
|
||||
{
|
||||
ar & history.m_initial_state_ids;
|
||||
ar & history.m_last_active_state_ids;
|
||||
}
|
||||
|
||||
@@ -210,17 +208,17 @@ void serialize(Archive& ar,
|
||||
msm::backmp11::detail::serialize(ar, sm);
|
||||
}
|
||||
|
||||
template<typename T, int N>
|
||||
template<typename T, typename InitialStateIds>
|
||||
void serialize(T& ar,
|
||||
msm::backmp11::detail::history_impl<msm::front::no_history, N>& history,
|
||||
msm::backmp11::detail::history_impl<msm::front::no_history, InitialStateIds>& history,
|
||||
const unsigned int /*version*/)
|
||||
{
|
||||
msm::backmp11::detail::serialize(ar, history);
|
||||
}
|
||||
|
||||
template<typename T, typename... Es, int N>
|
||||
template<typename T, typename... Es, typename InitialStateIds>
|
||||
void serialize(T& ar,
|
||||
msm::backmp11::detail::history_impl<msm::front::shallow_history<Es...>, N>& history,
|
||||
msm::backmp11::detail::history_impl<msm::front::shallow_history<Es...>, InitialStateIds>& history,
|
||||
const unsigned int /*version*/)
|
||||
{
|
||||
msm::backmp11::detail::serialize(ar, history);
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
// Copyright 2025 Christian Granzin
|
||||
// Copyright 2010 Christophe Henry
|
||||
// henry UNDERSCORE christophe AT hotmail DOT com
|
||||
// This is an extended version of the state machine available in the boost::mpl library
|
||||
// Distributed under the same license as the original.
|
||||
// Copyright for the original version:
|
||||
// Copyright 2005 David Abrahams and Aleksey Gurtovoy. Distributed
|
||||
// under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#ifndef BOOST_MSM_NONSTANDALONE_TEST
|
||||
#define BOOST_TEST_MODULE backmp11_history_test
|
||||
#endif
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
// back-end
|
||||
#include "Backmp11.hpp"
|
||||
//front-end
|
||||
#include "FrontCommon.hpp"
|
||||
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace msm = boost::msm;
|
||||
namespace mp11 = boost::mp11;
|
||||
|
||||
using namespace msm::front;
|
||||
using namespace msm::backmp11;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Events.
|
||||
struct EnterSubmachine {};
|
||||
struct EnterSubmachineWithoutHistory {};
|
||||
struct ExitSubmachine {};
|
||||
struct EnterSubstate1 {};
|
||||
struct DeferredEvent {};
|
||||
|
||||
// States.
|
||||
struct State0 : public test::StateBase {};
|
||||
struct Substate0 : public test::StateBase
|
||||
{
|
||||
using deferred_events = mp11::mp_list<DeferredEvent>;
|
||||
};
|
||||
struct Substate1 : public test::StateBase
|
||||
{
|
||||
using deferred_events = mp11::mp_list<DeferredEvent>;
|
||||
};
|
||||
|
||||
struct default_config : state_machine_config
|
||||
{
|
||||
};
|
||||
struct favor_compile_time_config : default_config
|
||||
{
|
||||
using compile_policy = favor_compile_time;
|
||||
};
|
||||
|
||||
template <typename History, typename Config = default_config>
|
||||
struct hierarchical_state_machine
|
||||
{
|
||||
struct Submachine_ : test::StateMachineBase_<Submachine_>
|
||||
{
|
||||
using history = History;
|
||||
|
||||
using initial_state = Substate0;
|
||||
|
||||
using transition_table = mp11::mp_list<
|
||||
Row<Substate0, EnterSubstate1, Substate1>,
|
||||
Row<Substate0, DeferredEvent , none >
|
||||
>;
|
||||
};
|
||||
using Submachine = StateMachine<Submachine_, Config>;
|
||||
|
||||
struct UpperMachine_ : test::StateMachineBase_<UpperMachine_>
|
||||
{
|
||||
using initial_state = State0;
|
||||
|
||||
using transition_table = mp11::mp_list<
|
||||
Row<State0 , EnterSubmachine , Submachine>,
|
||||
Row<State0 , EnterSubmachineWithoutHistory, Submachine>,
|
||||
Row<Submachine, ExitSubmachine , State0 >
|
||||
>;
|
||||
};
|
||||
using UpperMachine = StateMachine<UpperMachine_, Config>;
|
||||
};
|
||||
|
||||
using TestMachinesAlwaysShallowHistory = boost::mpl::vector<
|
||||
hierarchical_state_machine<always_shallow_history>,
|
||||
hierarchical_state_machine<always_shallow_history, favor_compile_time_config>
|
||||
>;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(always_shallow_history, TestMachine, TestMachinesAlwaysShallowHistory)
|
||||
{
|
||||
using StateMachine = typename TestMachine::UpperMachine;
|
||||
using Submachine = typename TestMachine::Submachine;
|
||||
StateMachine state_machine;
|
||||
auto& submachine = state_machine.template get_state<Submachine>();
|
||||
|
||||
state_machine.start();
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<State0>());
|
||||
|
||||
state_machine.process_event(EnterSubmachine{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<Substate0>());
|
||||
// Push to submachine's event pool explicitly to test clearance.
|
||||
submachine.process_event(DeferredEvent{});
|
||||
BOOST_REQUIRE(submachine.get_pending_events().size() == 1);
|
||||
state_machine.process_event(EnterSubstate1{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<Substate1>());
|
||||
|
||||
// Re-entry with history
|
||||
state_machine.process_event(ExitSubmachine{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<State0>());
|
||||
state_machine.process_event(EnterSubmachine{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<Substate1>());
|
||||
BOOST_REQUIRE(submachine.get_pending_events().size() == 1);
|
||||
}
|
||||
|
||||
using history = shallow_history<EnterSubmachine>;
|
||||
using TestMachinesShallowHistory = boost::mpl::vector<
|
||||
hierarchical_state_machine<history>,
|
||||
hierarchical_state_machine<history, favor_compile_time_config>
|
||||
>;
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE(shallow_history, TestMachine, TestMachinesShallowHistory)
|
||||
{
|
||||
using StateMachine = typename TestMachine::UpperMachine;
|
||||
using Submachine = typename TestMachine::Submachine;
|
||||
StateMachine state_machine;
|
||||
auto& submachine = state_machine.template get_state<Submachine>();
|
||||
|
||||
state_machine.start();
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<State0>());
|
||||
|
||||
state_machine.process_event(EnterSubmachine{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<Substate0>());
|
||||
// Push to submachine's event pool explicitly to test clearance.
|
||||
submachine.process_event(DeferredEvent{});
|
||||
BOOST_REQUIRE(submachine.get_pending_events().size() == 1);
|
||||
state_machine.process_event(EnterSubstate1{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<Substate1>());
|
||||
|
||||
// Re-entry with history
|
||||
state_machine.process_event(ExitSubmachine{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<State0>());
|
||||
state_machine.process_event(EnterSubmachine{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<Substate1>());
|
||||
BOOST_REQUIRE(submachine.get_pending_events().size() == 1);
|
||||
|
||||
// Re-entry without history
|
||||
state_machine.process_event(ExitSubmachine{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<State0>());
|
||||
state_machine.process_event(EnterSubmachineWithoutHistory{});
|
||||
BOOST_REQUIRE(state_machine.template is_state_active<Substate0>());
|
||||
BOOST_REQUIRE(submachine.get_pending_events().size() == 0);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -84,6 +84,7 @@ add_executable(boost_msm_cxx17_tests
|
||||
Backmp11Deferred.cpp
|
||||
Backmp11EntryExit.cpp
|
||||
Backmp11FunctorApi.cpp
|
||||
Backmp11History.cpp
|
||||
Backmp11ManyDeferTransitions.cpp
|
||||
Backmp11RootSm.cpp
|
||||
Backmp11Transitions.cpp
|
||||
|
||||
+2
-1
@@ -82,8 +82,9 @@ test-suite msm-unit-tests-cxxstd17
|
||||
[ run Backmp11Context.cpp ]
|
||||
[ run Backmp11CopyMove.cpp ]
|
||||
[ run Backmp11Deferred.cpp ]
|
||||
[ run Backmp11FunctorApi.cpp ]
|
||||
[ run Backmp11EntryExit.cpp ]
|
||||
[ run Backmp11FunctorApi.cpp ]
|
||||
[ run Backmp11History.cpp ]
|
||||
[ run Backmp11ManyDeferTransitions.cpp ]
|
||||
[ run Backmp11RootSm.cpp ]
|
||||
[ run Backmp11Transitions.cpp ]
|
||||
|
||||
+18
-106
@@ -8,14 +8,16 @@
|
||||
// file LICENSE_1_0.txt or copy at
|
||||
// http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
// back-end
|
||||
#include "BackCommon.hpp"
|
||||
//front-end
|
||||
#include <boost/msm/front/state_machine_def.hpp>
|
||||
#ifndef BOOST_MSM_NONSTANDALONE_TEST
|
||||
#define BOOST_TEST_MODULE serialize_with_history_test
|
||||
#endif
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
// back-end
|
||||
#include "BackCommon.hpp"
|
||||
//front-end
|
||||
#include "FrontCommon.hpp"
|
||||
|
||||
// include headers that implement a archive in simple text format
|
||||
#include <boost/archive/text_oarchive.hpp>
|
||||
#include <boost/archive/text_iarchive.hpp>
|
||||
@@ -44,11 +46,11 @@ namespace
|
||||
std::string name;
|
||||
};
|
||||
|
||||
template<template <typename...> class Back, typename Policy = void>
|
||||
template <template <typename...> class Back, typename Policy = void>
|
||||
struct hierarchical_state_machine
|
||||
{
|
||||
// front-end: define the FSM structure
|
||||
struct player_ : public msm::front::state_machine_def<player_>
|
||||
struct player_ : public msm::front::test::StateMachineBase_<player_>
|
||||
{
|
||||
BOOST_MSM_TEST_DEFINE_DEPENDENT_TEMPLATES(player_)
|
||||
|
||||
@@ -60,84 +62,37 @@ namespace
|
||||
can_close_drawer_counter(0)
|
||||
{}
|
||||
// The list of FSM states
|
||||
struct Empty : public msm::front::state<>
|
||||
struct Empty : msm::front::test::StateBase
|
||||
{
|
||||
template <class Event,class FSM>
|
||||
void on_entry(Event const&,FSM& ) {++entry_counter;}
|
||||
template <class Event,class FSM>
|
||||
void on_exit(Event const&,FSM& ) {++exit_counter;}
|
||||
int entry_counter;
|
||||
int exit_counter;
|
||||
};
|
||||
struct Open : public msm::front::state<>
|
||||
struct Open : msm::front::test::StateBase
|
||||
{
|
||||
template <class Event,class FSM>
|
||||
void on_entry(Event const&,FSM& ) {++entry_counter;}
|
||||
template <class Event,class FSM>
|
||||
void on_exit(Event const&,FSM& ) {++exit_counter;}
|
||||
int entry_counter;
|
||||
int exit_counter;
|
||||
};
|
||||
|
||||
// sm_ptr still supported but deprecated as functors are a much better way to do the same thing
|
||||
struct Stopped : public msm::front::state<>
|
||||
struct Stopped : msm::front::test::StateBase
|
||||
{
|
||||
template <class Event,class FSM>
|
||||
void on_entry(Event const&,FSM& ) {++entry_counter;}
|
||||
template <class Event,class FSM>
|
||||
void on_exit(Event const&,FSM& ) {++exit_counter;}
|
||||
int entry_counter;
|
||||
int exit_counter;
|
||||
};
|
||||
|
||||
struct Playing_ : public msm::front::state_machine_def<Playing_>
|
||||
struct Playing_ : public msm::front::test::StateMachineBase_<Playing_>
|
||||
{
|
||||
BOOST_MSM_TEST_DEFINE_DEPENDENT_TEMPLATES(Playing_)
|
||||
|
||||
// History for backmp11
|
||||
using history = msm::front::shallow_history<end_pause>;
|
||||
|
||||
template <class Event,class FSM>
|
||||
void on_entry(Event const&,FSM& ) {++entry_counter;}
|
||||
template <class Event,class FSM>
|
||||
void on_exit(Event const&,FSM& ) {++exit_counter;}
|
||||
int entry_counter;
|
||||
int exit_counter;
|
||||
unsigned int start_next_song_counter;
|
||||
unsigned int start_prev_song_guard_counter;
|
||||
|
||||
Playing_():
|
||||
start_next_song_counter(0),
|
||||
start_prev_song_guard_counter(0)
|
||||
{}
|
||||
unsigned int start_next_song_counter{};
|
||||
unsigned int start_prev_song_guard_counter{};
|
||||
|
||||
// The list of FSM states
|
||||
struct Song1 : public msm::front::state<>
|
||||
struct Song1 : msm::front::test::StateBase
|
||||
{
|
||||
template <class Event,class FSM>
|
||||
void on_entry(Event const&,FSM& ) {++entry_counter;}
|
||||
template <class Event,class FSM>
|
||||
void on_exit(Event const&,FSM& ) {++exit_counter;}
|
||||
int entry_counter;
|
||||
int exit_counter;
|
||||
};
|
||||
struct Song2 : public msm::front::state<>
|
||||
struct Song2 : msm::front::test::StateBase
|
||||
{
|
||||
template <class Event,class FSM>
|
||||
void on_entry(Event const&,FSM& ) {++entry_counter;}
|
||||
template <class Event,class FSM>
|
||||
void on_exit(Event const&,FSM& ) {++exit_counter;}
|
||||
int entry_counter;
|
||||
int exit_counter;
|
||||
};
|
||||
struct Song3 : public msm::front::state<>
|
||||
struct Song3 : msm::front::test::StateBase
|
||||
{
|
||||
template <class Event,class FSM>
|
||||
void on_entry(Event const&,FSM& ) {++entry_counter;}
|
||||
template <class Event,class FSM>
|
||||
void on_exit(Event const&,FSM& ) {++exit_counter;}
|
||||
int entry_counter;
|
||||
int exit_counter;
|
||||
};
|
||||
// the initial state. Must be defined
|
||||
typedef Song1 initial_state;
|
||||
@@ -158,25 +113,13 @@ namespace
|
||||
g_row < Song3 , PreviousSong, Song2 ,&pl::start_prev_song_guard>
|
||||
// +---------+-------------+---------+---------------------+----------------------+
|
||||
> {};
|
||||
// Replaces the default no-transition response.
|
||||
template <class FSM,class Event>
|
||||
void no_transition(Event const&, FSM&,int)
|
||||
{
|
||||
BOOST_FAIL("no_transition called!");
|
||||
}
|
||||
};
|
||||
// back-end
|
||||
typedef Back<Playing_,Policy,msm::back::ShallowHistory<mpl::vector<end_pause> > > Playing;
|
||||
|
||||
// state not defining any entry or exit
|
||||
struct Paused : public msm::front::state<>
|
||||
struct Paused : msm::front::test::StateBase
|
||||
{
|
||||
template <class Event,class FSM>
|
||||
void on_entry(Event const&,FSM& ) {++entry_counter;}
|
||||
template <class Event,class FSM>
|
||||
void on_exit(Event const&,FSM& ) {++exit_counter;}
|
||||
int entry_counter;
|
||||
int exit_counter;
|
||||
};
|
||||
|
||||
// the initial state of the player SM. Must be defined
|
||||
@@ -223,43 +166,12 @@ namespace
|
||||
a_row < Paused , open_close , Open , &p::stop_and_open >
|
||||
// +---------+-------------+---------+---------------------+----------------------+
|
||||
> {};
|
||||
// Replaces the default no-transition response.
|
||||
template <class FSM,class Event>
|
||||
void no_transition(Event const&, FSM&,int)
|
||||
{
|
||||
BOOST_FAIL("no_transition called!");
|
||||
}
|
||||
// init counters
|
||||
template <class Event,class FSM>
|
||||
void on_entry(Event const&,FSM& fsm)
|
||||
{
|
||||
fsm.template get_state<typename player_::Stopped&>().entry_counter=0;
|
||||
fsm.template get_state<typename player_::Stopped&>().exit_counter=0;
|
||||
fsm.template get_state<typename player_::Open&>().entry_counter=0;
|
||||
fsm.template get_state<typename player_::Open&>().exit_counter=0;
|
||||
fsm.template get_state<typename player_::Empty&>().entry_counter=0;
|
||||
fsm.template get_state<typename player_::Empty&>().exit_counter=0;
|
||||
fsm.template get_state<typename player_::Playing&>().entry_counter=0;
|
||||
fsm.template get_state<typename player_::Playing&>().exit_counter=0;
|
||||
fsm.template get_state<typename player_::Playing&>().template get_state<typename player_::Playing::Song1&>().entry_counter=0;
|
||||
fsm.template get_state<typename player_::Playing&>().template get_state<typename player_::Playing::Song1&>().exit_counter=0;
|
||||
fsm.template get_state<typename player_::Playing&>().template get_state<typename player_::Playing::Song2&>().entry_counter=0;
|
||||
fsm.template get_state<typename player_::Playing&>().template get_state<typename player_::Playing::Song2&>().exit_counter=0;
|
||||
fsm.template get_state<typename player_::Playing&>().template get_state<typename player_::Playing::Song3&>().entry_counter=0;
|
||||
fsm.template get_state<typename player_::Playing&>().template get_state<typename player_::Playing::Song3&>().exit_counter=0;
|
||||
fsm.template get_state<typename player_::Paused&>().entry_counter=0;
|
||||
fsm.template get_state<typename player_::Paused&>().exit_counter=0;
|
||||
}
|
||||
|
||||
};
|
||||
typedef Back<player_, Policy> player;
|
||||
};
|
||||
// Pick a back-end
|
||||
typedef get_hierarchical_test_machines<hierarchical_state_machine> test_machines;
|
||||
|
||||
// static char const* const state_names[] = { "Stopped", "Open", "Empty", "Playing", "Paused" };
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE_TEMPLATE( serialize_with_history_test, test_machine, test_machines )
|
||||
{
|
||||
typename test_machine::player p;
|
||||
|
||||
Reference in New Issue
Block a user