I'm implementing a state machine as the following diagram state machine diagram
I trigger "event1" and "event2" to the state machine, when "event1" triggered, the state machine exit "state1" state, enter "state3" sub-state machine and stay at "state3-state1" state. When "event2" triggered, the "state3" sub-state machine enter the exit pointer and enter "state1" state. After enter "state1" state, I find the "guard" is called twice. The following is my code
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/internal_row.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <string>
#include <iostream>
using namespace std;
namespace msmf = boost::msm::front;
namespace msmb = boost::msm::back;
namespace mpl = boost::mpl;
struct event1 {};
struct event2 {};
struct S_ : public msmf::state_machine_def<S_>
{
typedef msmb::state_machine<S_> S;
struct guard
{
template <class Event,class FSM,class SourceState,class TargetState>
bool operator()(const Event& event, FSM&, SourceState&, TargetState&)
{
std::cout << "guard.\n";
return false;
}
};
struct State1 : public msmf::state<>
{
template<class Event, class FSM>
void on_entry(const Event&, FSM&)
{
cout << "entering state: <state1>\n";
}
template <class Event,class FSM>
void on_exit(const Event&, FSM&)
{
cout << "leaving state: <state1>\n";
}
};
struct State2 : public msmf::state<>
{
template<class Event, class FSM>
void on_entry(const Event&, FSM&)
{
cout << "entering state: <state2>\n";
}
template <class Event,class FSM>
void on_exit(const Event&, FSM&)
{
cout << "leaving state: <state2>\n";
}
};
struct State3_ : public msmf::state_machine_def<State3_>
{
template<class Event,class FSM>
void on_entry(const Event&, FSM&)
{
cout << "entering state: <state3>\n";
}
template <class Event,class FSM>
void on_exit(const Event&, FSM&)
{
cout << "leaving state: <state3>\n";
}
struct State1 : public msmf::state<>
{
template<class Event, class FSM>
void on_entry(const Event&, FSM&)
{
cout << "entering state: <state3-state1>\n";
}
template <class Event,class FSM>
void on_exit(const Event&, FSM&)
{
cout << "leaving state: <state3-state1>\n";
}
};
struct Exit: msmf::exit_pseudo_state<event2>
{
};
typedef State1 initial_state;
struct transition_table : mpl::vector<
msmf::Row<State1, event2, Exit, msmf::none, msmf::none>
>{};
};
typedef msmb::state_machine<State3_> State3;
typedef State1 initial_state;
struct transition_table : mpl::vector<
msmf::Row<State1, event1, State3, msmf::none, msmf::none>,
msmf::Row<State1, msmf::none, State2, msmf::none, guard>,
msmf::Row<State3::exit_pt<State3_::Exit>, event2, State1, msmf::none, msmf::none>
>{};
};
typedef msmb::state_machine<S_> S;
int main()
{
S s;
s.start();
s.process_event(event1());
s.process_event(event2());
return 0;
}
The version of boost is 1.66, the output of the program is
entering state: <state1>
guard.
leaving state: <state1>
entering state: <state3>
entering state: <state3-state1>
leaving state: <state3-state1>
leaving state: <state3>
entering state: <state1>
guard.
guard.
I find a easier dome to show the same question, the state machine diagram is here and the code is
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/internal_row.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <string>
#include <iostream>
using namespace std;
namespace msmf = boost::msm::front;
namespace msmb = boost::msm::back;
namespace mpl = boost::mpl;
struct event1 {};
struct event2 {};
struct S_ : public msmf::state_machine_def<S_>
{
typedef msmb::state_machine<S_> S;
struct guard
{
template <class Event,class FSM,class SourceState,class TargetState>
bool operator()(const Event& event, FSM&, SourceState&, TargetState&)
{
std::cout << "guard.\n";
return false;
}
};
struct State1 : public msmf::state<>
{
template<class Event, class FSM>
void on_entry(const Event&, FSM&)
{
cout << "entering state: <state1>\n";
}
template <class Event,class FSM>
void on_exit(const Event&, FSM&)
{
cout << "leaving state: <state1>\n";
}
};
struct State2_ : public msmf::state_machine_def<State2_>
{
template<class Event,class FSM>
void on_entry(const Event&, FSM& fsm)
{
cout << "entering state: <state2>\n";
}
template <class Event,class FSM>
void on_exit(const Event&, FSM&)
{
cout << "leaving state: <state2>\n";
}
struct State1 : public msmf::state<>
{
template<class Event, class FSM>
void on_entry(const Event&, FSM&)
{
cout << "entering state: <state2-state1>\n";
}
template <class Event,class FSM>
void on_exit(const Event&, FSM&)
{
cout << "leaving state: <state2-state1>\n";
}
};
struct State2 : public msmf::state<>
{
template<class Event, class FSM>
void on_entry(const Event&, FSM&)
{
cout << "entering state: <state2-state2>\n";
}
template <class Event,class FSM>
void on_exit(const Event&, FSM&)
{
cout << "leaving state: <state2-state2>\n";
}
};
typedef State1 initial_state;
struct transition_table : mpl::vector<
msmf::Row<State1, msmf::none, State2, msmf::none, guard>
>{};
};
typedef msmb::state_machine<State2_> State2;
typedef State1 initial_state;
struct transition_table : mpl::vector<
msmf::Row<State1, msmf::none, State2, msmf::none, msmf::none>
>{};
};
typedef msmb::state_machine<S_> S;
int main()
{
S s;
s.start();
return 0;
}
The output of the program is
entering state: <state1>
leaving state: <state1>
entering state: <state2>
entering state: <state2-state1>
guard.
guard.
I'm currently facing exactly the same problem using boost's msm in the latest version 1.79.0.
However a gut feeling tells me it's not a bug but most likely some conceptual misunderstanding on my side.
What I observed upon entering the sub-state machine was:
truethe states are changed/processed immediately as expected.falseit gets invoked a second time - like in the OP's example.I was playing around with the example and could get it somehow working as expected, however I don't think it's the advocated approach:
This now results in:
What's changed is the required event
event1to triggerState1toState2inside the sub-state machine while additionally having the guard in place. The event itself however gets raised / enqueued in theState1::on_entryhandler.I tried to find some hint on this in the documentation but was not really successful. To me I'm somewhere missing the point like "there's an additional event enqueued when entering a sub-state machine...". The above could be a 'fix', however I'm really not convinced of it myself and would love to see someone to shed some light on this...