1

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.
Passer By
  • 19,325
  • 6
  • 49
  • 96
szh
  • 41
  • 2

1 Answers1

0

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:

  1. As long as there are no events required from one state to another and guards always return true the states are changed/processed immediately as expected.
  2. As soon as there's an event 'in the way' that has not been triggered yet, execution stops and awaits the event as expected.
  3. If a guard is hit after entering the sub-state machine (without having (2) in the way before) and it returns false it 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:

#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& 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& fsm)
            {
                cout << "entering state: <state2-state1>\n";
                cout << "raising event 'event1'\n";
                fsm.process_event(event1()); // Immediately enqueue event1 to get out of this state...
            }

            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, event1, 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;
}

This now results in:

entering state: <state1>
leaving state: <state1>
entering state: <state2>
entering state: <state2-state1>
raising event 'event1'
guard.

What's changed is the required event event1 to trigger State1 to State2 inside the sub-state machine while additionally having the guard in place. The event itself however gets raised / enqueued in the State1::on_entry handler.

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...

Phlar
  • 33
  • 1
  • 6
  • Maybe you could investigate this a bit more and update your answer when your are sure? It is a bit vague right now :) – Lars Nielsen May 05 '22 at 05:54
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 05 '22 at 05:54
  • I will! I'd rather have liked to add my 'answer' as a comment but unfortunately I'm not allowed to as my reputation-score is below 50. – Phlar May 05 '22 at 06:33