-
Notifications
You must be signed in to change notification settings - Fork 201
Description
Today I wanted to update the Boost::SML library in our codebase, we used version 1.1.0 and I wanted to switch to 1.1.6. We have an extensive unit test suite and one of the tests failed.
We have the following state machine:
With a nested Playing machine:
All transitions are automatic, no events are needed. So in the unit test, you typically would set the result of the dice throw up front and when the state machine is instantiated, the test expected it to get into one of the final states immediately. So X or Success
This used to be the case with version 1.1.0, now that I'm trying to upgrade to 1.1.6 the endstate is Playing.Initial. This is especially weird to me, as the initial should always auto-transition.
We generate our state machines from the UML, here is the corresponding amalgamated code:
#ifndef SUBSTATEMACHINEEXITPOINT_STATE_MACHINE_HPP
#define SUBSTATEMACHINEEXITPOINT_STATE_MACHINE_HPP
#include <queue>
namespace SubStateMachineExitPoint {
class UserData
{
friend struct Callbacks;
friend struct Machine;
public:
int m_dice{1};
};
struct Callbacks
{
public:
Callbacks(UserData& userData) : m_data(userData){}
bool IsEven() { return m_data.m_dice % 2 == 0; }
UserData& m_data;
};
namespace _private {
namespace Events {
struct Lose : public boost::sml::utility::id_impl<2147483646>
{
static auto c_str() { return "Lose";}
};
struct Anonymous : public boost::sml::utility::id_impl<2147483645>
{
static auto c_str() { return "Anonymous";}
};
} // namespace Events
struct Lose
{
static auto c_str() { return "Lose"; }
};
} // namespace _private
namespace Playing {
/* states */
struct Initial
{
static auto c_str() { return "Initial"; }
};
struct ThrowDice
{
static auto c_str() { return "ThrowDice"; }
};
/* transition table */
struct Machine
{
static auto c_str() { return "Playing"; }
explicit Machine(UserData* data) : m_calls(*data) {}
auto operator()() const
{
using namespace boost;
using namespace boost::sml;
using namespace Events;
/* clang-format off */
return make_transition_table(
*sml::state<Initial> = sml::state<ThrowDice>,
sml::state<ThrowDice> [([=](){return !m_calls.IsEven();})] = sml::state<_private::Lose>,
sml::state<_private::Lose> / sml::process(_private::Events::Lose{}) = sml::X,
sml::state<ThrowDice> [([=](){return m_calls.IsEven();})] / process(_private::Events::Anonymous{}) = sml::X
);
/* clang-format on */
}
mutable Callbacks m_calls;
};
} // namespace Playing
/* states */
struct Initial
{
static auto c_str() { return "Initial"; }
};
struct Success
{
static auto c_str() { return "Success";}
};
/* transition table */
struct Machine
{
static auto c_str() { return "SubStateMachineExitPoint"; }
explicit Machine(UserData* data) : m_calls(*data) {}
auto operator()() const
{
using namespace boost;
using namespace boost::sml;
using namespace Events;
/* clang-format off */
return make_transition_table(
sml::state<Playing::Machine> + sml::event<_private::Events::Lose> = sml::X,
*sml::state<Initial> = sml::state<Playing::Machine>,
sml::state<Playing::Machine> + sml::event<_private::Events::Anonymous> = sml::state<Success>
);
/* clang-format on */
}
mutable Callbacks m_calls;
};
template <typename... SMPolicies>
auto MakeMachine(UserData& data) //-> boost::sml::sm<Machine, SMPolicies...>
{
return boost::sml::sm<Machine, boost::sml::process_queue<std::queue>, SMPolicies...>{
Machine{&data}, Playing::Machine{&data}};
}
template <typename Logger, typename... SMPolicies>
auto MakeMachine(
UserData& data,
Logger& logger) //-> boost::sml::sm<Machine, boost::sml::logger<Logger>, SMPolicies...>
{
return boost::sml::sm<Machine, boost::sml::process_queue<std::queue>,
boost::sml::logger<Logger>, SMPolicies...>{
Machine{&data}, Playing::Machine{&data}, logger};
}
} // namespace SubStateMachineExitPoint
#endif /* SUBSTATEMACHINEEXITPOINT_STATE_MACHINE_HPP */
the now-failing test then goes like this:
TEST(BoostSML, SubStateMachineExitPoint)
{
using namespace SubStateMachineExitPoint;
UserData data;
data.m_dice = 2;
auto winner = MakeMachine(data);
EXPECT_TRUE(winner.is(state<Success>));
data.m_dice = 1;
auto looser = MakeMachine(data);
EXPECT_TRUE(looser.is(sml::X));
}@krzysztof-jusiak any ideas on that?
Specifications
- Version: 1.1.6
- Platform: Linux Ubuntu 20.04

