1

i have some trouble implementing a sub-sub-statemachine with boost::msm. I'm trying to minimize my code here...

Test.cpp:

struct SM_ : StateMachineA<SM_> {};
// Pick a back-end
typedef boost::msm::back::state_machine<SM_> SM;
int main()
{
    std::cout << "Starting State Machine" << std::endl;
    SM sm1;
    //  sm1.start();
    return 0;
}

StateMachineA is defined in StateMachineA.h

namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;
template<typename Derived>
struct StateMachineA: protected msmf::state_machine_def < Derived, msmf::default_base_state >
        {
        public:

            //// Entry point to state machine. 

            //// Set initial state
            typedef mpl::vector<initState, allOk> initial_state;

            //// Exit Point
            struct Exit :msmf::terminate_state<> {};

            // ----- Sub State machine
            struct SSM_ : StateMachineB<SSM_> {};

            // Pick a back-end
            typedef boost::msm::back::state_machine<SSM_> stateMachineB;


            //// Transition table
            struct transition_table : mpl::vector<
                msmf::Row < initState, go, stateMachineB, msmf::none, msmf::none >,
                msmf::Row < allOk, fatalThrown, Exit, msmf::none, msmf::none >,
                msmf::Row < error, fatalThrown, Exit, msmf::none, msmf::none >
            > {};




        protected:


            template <class FSM, class Event>
            void no_transition(Event const&, FSM&, int)
            {
                std::cout << "ERROR: Unallowed transition detected" << std::endl;
            }
        };

StateMachineB has a StateMachineC included using the exact same code (replace B by C...).

Putting StateMachineC as a submachine to StateMachineA (omitting StateMachineB) works fine. Same for A -> B without including C works fine as well. Reordering the state machines (A -> C -> B) produces the same error. To sum up: Every combination of two state machines is working, every combination of three state machines is failing. The error occurs when having SM sm1; in my main function. -> While resolving the templates? Without that line, everything compiles fine.

The error log is lengthy (long enough to cause visual studio to crash when hovering it...). First error is:

D:\boost_1_59_0\boost/mpl/aux_/push_front_impl.hpp(45) : error C2664: 'int    boost::mpl::assertion_failed<false>(boost::mpl::assert<false>::type)' : cannot convert argument 1 from 'boost::mpl::failed ************(__thiscall boost::mpl::push_front_impl<boost::mpl::aux::vector_tag<20>>::apply<Sequence,T>::REQUESTED_PUSH_FRONT_SPECIALIZATION_FOR_SEQUENCE_DOES_NOT_EXIST::* ***********)(Sequence)' to 'boost::mpl::assert<false>::type'

with ... and around 200 "with" lines to follow. After that, many errors of type:

D:\boost_1_59_0\boost/mpl/aux_/insert_impl.hpp(60) : error C3203: 'type' : unspecialized class template can't be used as a template argument for template parameter 'State', expected a real type
D:\boost_1_59_0\boost/mpl/insert.hpp(32) : error C2903: 'apply' : symbol is neither a class template nor a function template
    D:\boost_1_59_0\boost/mpl/aux_/has_type.hpp(20) : see reference to class template instantiation 'boost::mpl::insert<U1,U2,U3>' being compiled

follow.

Any ideas?

Thanks!

ARTcrime
  • 110
  • 3
  • 12

1 Answers1

3

I couldn't reproduce your situation but I can show you how to implement SubSub state machine. Here is a document that I wrote. It describes about sub state machine. http://redboltz.wikidot.com/sub-machine-state

To implement SubSub state machine, just apply sub machine implementation twice.

Here is a code that contains SubSub state machine:

#include <iostream>
#include <boost/msm/back/state_machine.hpp>

#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <boost/static_assert.hpp>

namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;

// StateMachine [Osm]
//
// (initial)
//    |
//    V
// State1:StateSub --Event1--> State2
//
//
// StateMachine [StateSub]
//
// (initial)
//    |
//    V
// SubState1 --Event2--> SubState2:StateSubSub
//    A                          |
//    +--------Event3------------+
//
//
// StateMachine [StateSubSub]
//
// (initial)
//    |
//    V
// SubSubState1---Event4--> SubSubState2
//    A                          |
//    +-----------Event5---------+



// ----- Events
struct Event1 {};
struct Event2 {};
struct Event3 {};
struct Event4 {};
struct Event5 {};

// ----- State machine
struct StateSubSub_:msmf::state_machine_def<StateSubSub_>
{
    struct SubSubState1:msmf::state<> {
        template <class Event,class Fsm>
        void on_entry(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSubSub_>::value));
            std::cout << "SubSubState1::on_entry()" << std::endl;
        }
        template <class Event,class Fsm>
        void on_exit(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSubSub_>::value));
            std::cout << "SubSubState1::on_exit()" << std::endl;
        }
    };
    struct SubSubState2:msmf::state<> {
        template <class Event,class Fsm>
        void on_entry(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSubSub_>::value));
            std::cout << "SubSubState2::on_entry()" << std::endl;
        }
        template <class Event,class Fsm>
        void on_exit(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSubSub_>::value));
            std::cout << "SubSubState2::on_exit()" << std::endl;
        }
    };

    // Set initial state
    typedef mpl::vector<SubSubState1> initial_state;
    // Transition table
    struct transition_table:mpl::vector<
        //          Start      Event   Next       Action      Guard
        msmf::Row < SubSubState1, Event4, SubSubState2, msmf::none, msmf::none >,
        msmf::Row < SubSubState2, Event5, SubSubState1, msmf::none, msmf::none >
    > {};
};
typedef msm::back::state_machine<StateSubSub_> StateSubSub;


// ----- State machine
struct StateSub_:msmf::state_machine_def<StateSub_>
{
    struct SubState1:msmf::state<> {
        template <class Event,class Fsm>
        void on_entry(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSub_>::value));
            std::cout << "SubState1::on_entry()" << std::endl;
        }
        template <class Event,class Fsm>
        void on_exit(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSub_>::value));
            std::cout << "SubState1::on_exit()" << std::endl;
        }
    };
    struct SubState2_:msmf::state_machine_def<SubState2_>
    {
        template <class Event,class Fsm>
        void on_entry(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSub_>::value));
            std::cout << "SubState2::on_entry()" << std::endl;
        }
        template <class Event,class Fsm>
        void on_exit(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, StateSub_>::value));
            std::cout << "SubState2::on_exit()" << std::endl;
        }
        struct Impl_:StateSubSub {};
        typedef Impl_ initial_state;
    };
    // Pick a back-end
    typedef msm::back::state_machine<SubState2_> SubState2;

    // Set initial state
    typedef mpl::vector<SubState1> initial_state;
    // Transition table
    struct transition_table:mpl::vector<
        //          Start      Event   Next       Action      Guard
        msmf::Row < SubState1, Event2, SubState2, msmf::none, msmf::none >,
        msmf::Row < SubState2, Event3, SubState1, msmf::none, msmf::none >
    > {};
};
typedef msm::back::state_machine<StateSub_> StateSub;

struct OuterSm_:msmf::state_machine_def<OuterSm_>
{
    struct State1_:msmf::state_machine_def<State1_>
    {
        template <class Event,class Fsm>
        void on_entry(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
            std::cout << "State1::on_entry()" << std::endl;
        }
        template <class Event,class Fsm>
        void on_exit(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
            std::cout << "State1::on_exit()" << std::endl;
        }
        struct Impl_:StateSub {};
        typedef Impl_ initial_state;
    };
    // Pick a back-end
    typedef msm::back::state_machine<State1_> State1;
    struct State2:msmf::state<>
    {
        template <class Event,class Fsm>
        void on_entry(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
            std::cout << "State2::on_entry()" << std::endl;
        }
        template <class Event,class Fsm>
        void on_exit(Event const&, Fsm&) const {
            BOOST_STATIC_ASSERT((boost::is_convertible<Fsm, OuterSm_>::value));
            std::cout << "State2::on_exit()" << std::endl;
        }
    };
    // Set initial state
    typedef State1 initial_state;
    // Transition table
    struct transition_table:mpl::vector<
        //          Start   Event   Next    Action      Guard
        msmf::Row < State1, Event1, State2, msmf::none, msmf::none >
    > {};
};

// Pick a back-end
typedef msm::back::state_machine<OuterSm_> Osm;

void test()
{
    Osm osm;
    osm.start();

    std::cout << "> Send Event2()" << std::endl;
    osm.process_event(Event2());
    std::cout << "> Send Event4()" << std::endl;
    osm.process_event(Event4());
    std::cout << "> Send Event5()" << std::endl;
    osm.process_event(Event5());
    std::cout << "> Send Event3()" << std::endl;
    osm.process_event(Event3());
    std::cout << "> Send Event1()" << std::endl;
    osm.process_event(Event1());
}

int main()
{
    test();
}

You can compile, run and modify it on Wandbox, online-compiler.

https://wandbox.org/permlink/tBCSQDNhkBQkXxh0

Moritz Barsnick
  • 25
  • 1
  • 14
Takatoshi Kondo
  • 3,111
  • 17
  • 36
  • Hi, thanks a lot for the answer. I've already found the entry at redboltz and used it a lot. Thanks for that too! – ARTcrime Mar 30 '16 at 06:38
  • Hi, thanks a lot for the answer. I've already found the entry at redboltz and used it a lot. Thanks for that too! One problem I had was the limit of mpl::vector. When adding submachines to a statmachine, I think the véctors get merged internally to a bigger transition table.. Hence, the sum of all transitions must be below the limit, not only the number of transitions for every single statemachine. I've merged two statemachines and the error was much more meaningful. Not sure if I fixed something else along the merging though.... – ARTcrime Mar 30 '16 at 06:45
  • I think that mpl::vector's limit applies to each state machine individually. See http://melpon.org/wandbox/permlink/SK5dIbEiSXi6pqHk I limited the max mpl::vector size to 10. And each (sub) state machine has a transition table that contains 10 elements. – Takatoshi Kondo Mar 30 '16 at 20:16
  • OK, in that case, I must have corrected s.th. unintentionally while merging the two state machines... I'll check the version history and see if I can find the problem. Thanks a lot for your help! – ARTcrime Apr 01 '16 at 14:20
  • Plus one for this code, as the documentation for boost 1.61 is completely outdated and, including the examples, incorrect. Thanks! – gilgamash Jun 08 '16 at 14:24