Skip to content
Merged
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
1 change: 1 addition & 0 deletions doc/modules/ROOT/pages/version-history.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* Bug fixes (`backmp11`):
** Incorrect `FSM` type in calls to `on_entry(...)` and `on_exit(...)` (https://github.com/boostorg/msm/issues/167[#167])
** Completion events fire too often (https://github.com/boostorg/msm/issues/166[#166])
** Transitions in hierarchical state machines are not selected correctly (favor_compile_time) (https://github.com/boostorg/msm/issues/200[#200])
* **Breaking change (`backmp11`)**: Direct access to the event pool is changed from `public` to `protected`,
because manipulating it outside of the library code can lead to undefined behavior.

Expand Down
4 changes: 2 additions & 2 deletions include/boost/msm/backmp11/detail/basic_polymorphic.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,13 +277,13 @@ class basic_polymorphic
{
return basic_polymorphic{std::in_place_type<U>,
std::forward<Args>(args)...};
};
}

template <typename U>
static basic_polymorphic make(const U& obj)
{
return basic_polymorphic{obj};
};
}

T* get() const noexcept
{
Expand Down
7 changes: 3 additions & 4 deletions include/boost/msm/backmp11/favor_compile_time.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -207,10 +207,9 @@ struct compile_policy_impl<favor_compile_time>
{
public:
template<typename StateMachine>
process_result execute(StateMachine& sm, int region_id, any_event const& event) const
process_result execute(StateMachine& sm, int region_id, any_event const& event, process_result result) const
{
using cell_t = process_result (*)(StateMachine&, int, any_event const&);
process_result result = process_result::HANDLED_FALSE;
for (const generic_cell cell : m_transition_cells)
{
result |= reinterpret_cast<cell_t>(cell)(sm, region_id, event);
Expand Down Expand Up @@ -370,15 +369,15 @@ struct compile_policy_impl<favor_compile_time>
if (m_call_process_event)
{
result = m_call_process_event(sm, event);
if (result)
if (result & handled_true_or_deferred)
{
return result;
}
}
auto it = m_transition_chains.find(event.type());
if (it != m_transition_chains.end())
{
result = (it->second.execute)(sm, region_id, event);
result = (it->second.execute)(sm, region_id, event, result);
}
return result;
}
Expand Down
4 changes: 2 additions & 2 deletions test/Backmp11Context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,13 @@ struct MachineBase_ : public state_machine_def<MachineBase_<T>>
{
Context& context = get_context(fsm);
context.machine_entries++;
};
}

template <typename Event, typename Fsm>
void on_exit(const Event& /*event*/, Fsm& fsm)
{
get_context(fsm).machine_exits++;
};
}

using initial_state = Default;
};
Expand Down
5 changes: 5 additions & 0 deletions test/Backmp11Deferred.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ using namespace boost::msm::backmp11;

namespace mp11 = boost::mp11;

namespace
{

// Events
struct Event1
{
Expand Down Expand Up @@ -438,3 +441,5 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(action_deferred, Fsm, Fsms)
}

} // namespace action_deferred

} // namespace
5 changes: 5 additions & 0 deletions test/Backmp11ManyDeferTransitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ using namespace boost::msm::front;
using namespace boost::msm::backmp11;
namespace mp11 = boost::mp11;

namespace
{

// Events
struct Event1 {};
struct Event2 {};
Expand Down Expand Up @@ -235,3 +238,5 @@ BOOST_AUTO_TEST_CASE_TEMPLATE(action_defer_transitions, TestMachine, TestMachine
}

} // namespace action_defer_transitions

} // namespace
8 changes: 4 additions & 4 deletions test/Backmp11RootSm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ struct hierarchical_machine
static_assert(std::is_same_v<Fsm,RootSm>);
}
fsm.get_root_sm().machine_entries++;
};
}

template <typename Event, typename Fsm>
void on_exit(const Event& /*event*/, Fsm& fsm)
Expand All @@ -106,7 +106,7 @@ struct hierarchical_machine
static_assert(std::is_same_v<Fsm,RootSm>);
}
fsm.get_root_sm().machine_exits++;
};
}

using initial_state = Default;
};
Expand All @@ -128,7 +128,7 @@ struct hierarchical_machine
// static_assert(std::is_same_v<Fsm,MiddleMachine>);
}
fsm.get_root_sm().machine_entries++;
};
}

template <typename Event, typename Fsm>
void on_exit(const Event& /*event*/, Fsm& fsm)
Expand All @@ -142,7 +142,7 @@ struct hierarchical_machine
// static_assert(std::is_same_v<Fsm,MiddleMachine>);
}
fsm.get_root_sm().machine_exits++;
};
}
};
using LowerMachine = state_machine<LowerMachine_, SmConfig>;

Expand Down
178 changes: 114 additions & 64 deletions test/Backmp11Transitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,19 @@
// 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_transitions_test
#endif
#include <boost/test/unit_test.hpp>

// back-end
#include <boost/msm/backmp11/state_machine.hpp>
#include <boost/msm/backmp11/favor_compile_time.hpp>
//front-end
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include "FrontCommon.hpp"

#include "Utils.hpp"

#ifndef BOOST_MSM_NONSTANDALONE_TEST
#define BOOST_TEST_MODULE backmp11_members_test
#endif
#include <boost/test/unit_test.hpp>

namespace msm = boost::msm;
namespace mp11 = boost::mp11;

Expand All @@ -32,125 +31,176 @@ using namespace msm::backmp11;
namespace
{

template <typename T>
struct Counter
{
size_t count{};
};

// Events.
struct EnterSubmachine{};
struct TriggerTransition{};
struct TriggerInternalTransition{};
struct TriggerInternalTransitionWithGuard
{
bool guard_value{};
};
struct TriggerSmInternalTransition{};
struct TriggerAnyTransition{};

// Actions
struct MyAction
{
template<typename Event, typename Fsm, typename Source, typename Target>
void operator()(const Event&, Fsm& fsm, Source&, Target&)
void operator()(const Event&, Fsm&, Source& source, Target&)
{
source.action_counter++;
}
};

// Guards
struct MyGuard
{
template<typename Fsm>
bool operator()(const TriggerInternalTransitionWithGuard& event, Fsm&) const
{
return event.guard_value;
}
};

struct NotMyGuard
{
template<typename Fsm>
bool operator()(const TriggerInternalTransitionWithGuard& event, Fsm&) const
{
std::get<Counter<Source>>(fsm.action_calls).count++;
return !event.guard_value;
}
};

// States.
struct MyState : public state<>{};
struct MyOtherState : public state<>{};
struct MyState : public test::StateBase{};
struct MyOtherState : public test::StateBase{};
class StateMachine;
struct StateMachine_;

using Counters = std::tuple<
Counter<MyState>,
Counter<MyOtherState>,
Counter<StateMachine_>,
Counter<StateMachine>>;

struct SmConfig : state_machine_config
struct default_config : state_machine_config
{
using root_sm = StateMachine;
// using compile_policy = favor_compile_time;
// using root_sm = StateMachine;
template <typename T>
using event_container = no_event_container<T>;
};

struct StateMachine_ : public state_machine_def<StateMachine_>
struct favor_compile_time_config : default_config
{
using activate_deferred_events = int;
using compile_policy = favor_compile_time;
};

template <typename Event, typename Fsm>
void on_entry(const Event& /*event*/, Fsm& fsm)
{
fsm.entry_calls++;
};
template <typename Config = default_config>
struct hierarchical_state_machine
{

template <typename Event, typename Fsm>
void on_exit(const Event& /*event*/, Fsm& fsm)
{
fsm.exit_calls++;
};
struct Submachine_ : public test::StateMachineBase_<Submachine_>
{
using initial_state = MyState;
using transition_table = mp11::mp_list<
Row<MyState, TriggerInternalTransitionWithGuard, none, MyAction, MyGuard>
>;
};
using Submachine = state_machine<Submachine_, Config>;

struct StateMachine_ : public test::StateMachineBase_<StateMachine_>
{
using initial_state = MyState;
using transition_table = mp11::mp_list<
Row<MyState , TriggerInternalTransition , none , MyAction, none>,
Row<MyState , TriggerSmInternalTransition, none , MyAction, none>,
Row<MyState , TriggerTransition , MyOtherState, MyAction, none>
Row<MyState , TriggerInternalTransition , none , MyAction, none>,
Row<MyState , TriggerInternalTransitionWithGuard, none , MyAction, NotMyGuard>,
Row<MyState , TriggerSmInternalTransition , none , MyAction, none>,
Row<MyState , TriggerTransition , MyOtherState, MyAction, none>,
Row<MyState , EnterSubmachine , Submachine , none , none>,
Row<Submachine, TriggerInternalTransitionWithGuard, Submachine , MyAction, NotMyGuard>
>;
using internal_transition_table = mp11::mp_list<
Internal<std::any , MyAction, none>,
Internal<TriggerSmInternalTransition, MyAction, none>
>;
};

// Leave this class without inheriting constructors to check
// that this only needs to be done in case we use a context
// (for convenience).
class StateMachine : public state_machine<StateMachine_, SmConfig, StateMachine>
class StateMachine : public state_machine<StateMachine_, Config, StateMachine>
{
public:
size_t entry_calls{};
size_t exit_calls{};
Counters action_calls{};
};

BOOST_AUTO_TEST_CASE( backmp11_members_test )
};

using TestMachines = boost::mpl::vector<
hierarchical_state_machine<>,
hierarchical_state_machine<favor_compile_time_config>
>;


BOOST_AUTO_TEST_CASE_TEMPLATE(start_stop, StateMachine, TestMachines)
{
StateMachine test_machine;
using TestMachine = typename StateMachine::StateMachine;
TestMachine test_machine;

test_machine.start();
BOOST_REQUIRE(test_machine.entry_calls == 1);
BOOST_REQUIRE(test_machine.entry_counter == 1);

if constexpr (std::is_same_v<
typename StateMachine::config_t::compile_policy,
favor_runtime_speed>)
test_machine.stop();
BOOST_REQUIRE(test_machine.exit_counter == 1);
}

BOOST_AUTO_TEST_CASE_TEMPLATE(transitions, StateMachine, TestMachines)
{
using TestMachine = typename StateMachine::StateMachine;
using compile_policy = typename TestMachine::config_t::compile_policy;
TestMachine test_machine;

test_machine.start();

if constexpr (std::is_same_v<compile_policy, favor_runtime_speed>)
{
test_machine.process_event(TriggerAnyTransition{});
ASSERT_ONE_AND_RESET(std::get<Counter<StateMachine>>(test_machine.action_calls).count);
ASSERT_ONE_AND_RESET(test_machine.action_counter);
}

test_machine.process_event(TriggerInternalTransition{});
ASSERT_ONE_AND_RESET(std::get<Counter<MyState>>(test_machine.action_calls).count);
ASSERT_ONE_AND_RESET(test_machine.template get_state<MyState>().action_counter);

// Ensure MyState consumes the event instead of StateMachine.
test_machine.process_event(TriggerSmInternalTransition{});
ASSERT_ONE_AND_RESET(std::get<Counter<MyState>>(test_machine.action_calls).count);
ASSERT_ONE_AND_RESET(test_machine.template get_state<MyState>().action_counter);


test_machine.process_event(TriggerTransition{});
ASSERT_ONE_AND_RESET(std::get<Counter<MyState>>(test_machine.action_calls).count);
BOOST_REQUIRE(test_machine.is_state_active<MyOtherState>());
ASSERT_ONE_AND_RESET(test_machine.template get_state<MyState>().action_counter);
BOOST_REQUIRE(test_machine.template is_state_active<MyOtherState>());

if constexpr (std::is_same_v<
typename StateMachine::config_t::compile_policy,
favor_runtime_speed>)
if constexpr (std::is_same_v<compile_policy, favor_runtime_speed>)
{
test_machine.process_event(TriggerAnyTransition{});
ASSERT_ONE_AND_RESET(std::get<Counter<StateMachine>>(test_machine.action_calls).count);
ASSERT_ONE_AND_RESET(test_machine.action_counter);
}

test_machine.process_event(TriggerSmInternalTransition{});
ASSERT_ONE_AND_RESET(std::get<Counter<StateMachine>>(test_machine.action_calls).count);
ASSERT_ONE_AND_RESET(test_machine.action_counter);

test_machine.stop();
}

BOOST_AUTO_TEST_CASE_TEMPLATE(submachine_transitions, StateMachine, TestMachines)
{
using TestMachine = typename StateMachine::StateMachine;
TestMachine test_machine;
auto& submachine = test_machine.template get_state<typename StateMachine::Submachine>();

test_machine.start();

test_machine.process_event(EnterSubmachine{});
ASSERT_ONE_AND_RESET(submachine.entry_counter);

test_machine.process_event(TriggerInternalTransitionWithGuard{false});
ASSERT_ONE_AND_RESET(submachine.action_counter);

test_machine.process_event(TriggerInternalTransitionWithGuard{true});
ASSERT_ONE_AND_RESET(submachine.template get_state<MyState>().action_counter);

test_machine.stop();
BOOST_REQUIRE(test_machine.exit_calls == 1);
}

} // namespace
Loading
Loading