1

I am trying to implement a simple protocol with boost::msm. As packets arrive they are processed and dispatched to the State Machine (SM) to be handled accordingly.

My pkt class (i.e. Pkt1) requires a handle to the fsm that would allow it to call fsm->process_event(...) (and of course i would add #include "myfsm.h" to the top of the pkt1.h).

So far so good. But what if my state machine (say State1) requires to react to that packet by sending a packet itself? Now I would include the "pkt1.h" header to the top of the "state1.h" so i could create an instance of the Pkt1 and call its send() function.

Well as you might guess this final inclusion leads to a "circular dependency"

The sample code (with the error) can be found : https://wandbox.org/permlink/IlFsUQyLPLrLl2RW (its my first time using wandbox, hope everything is OK)

Note) In the "state1.h" file remove the #include "pkt1.h" & on_entry(..)... Pkt1 pkt; pkt.send(); to make it compilable.

Questions:

1) How should I resolve this circular dependency?

2) I think the way forward would be to add an implementation file (.cpp) for my Pkt1 class and transfer the #include "myfsm.h" to this file, thus breaking the circular dependency. But how can I forward declare the MyFsm in the header file?

3) I am new to boost::msm/CRTP and the code is confusing to me. How can State1 get access to MyFsm while I have not included the corresponding header to state1.h?? (maybe because MyFsm derives from the functor front/back end which its header is included and allows virtual functions to call the corresponding MyFsm functions!!??)

Many thanks for your time and help in advance.

Code Included:

  • events.h

    #ifndef EVENTS
    #define EVENTS
    
    
    // ----- Events
    struct Event1 {};
    struct Event2 {};
    
    #endif // EVENTS
    
  • main.cpp

    #include <iostream>
    
    #include "events.h"
    #include "myfsm.h"
    #include "pkt1.h"
    
    int main()
    {
        MyFsm fsm;
        fsm.start();
    
        //fsm.process_event(Event1());
        Pkt1 rcvdPkt;
        rcvdPkt.dispatch(&fsm);
    
        return 0;
    }
    
  • myfsm.h

    //MyFsm.h
    #ifndef MYFSM
    #define MYFSM
    
    #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 "state1.h"
    #include "state2.h"
    #include "events.h"
    
    namespace msm = boost::msm;
    namespace msmf = boost::msm::front;
    namespace mpl = boost::mpl;
    
    
    struct MyFsm_ : msmf::state_machine_def<MyFsm_>
    {
        struct State1_ : State1 {}; // use public inheritance
        struct State2_ : State2 {}; // use public inheritance
    
       // Set initial state
       typedef State1_ initial_state;
    
       // Transition table
       struct transition_table:mpl::vector<
             msmf::Row < State1_, Event1, State2_, msmf::none, msmf::none >
       >{};
    };
    // Pick a back-end
    typedef msm::back::state_machine<MyFsm_> MyFsm;
    
    
    #endif // MYFSM
    
  • pkt1.h

    #ifndef PKT1
    #define PKT1
    
    #include "myfsm.h"
    #include "events.h"
    
    class Pkt1
    {
    public:
        Pkt1() {}
    
        void dispatch(MyFsm *fsm){
            fsm->process_event(Event1());
        }
    
        void send(){std::cout<<"pkt1 sent out ..."<<std::endl;}
    
    };
    
    #endif // PKT1
    
  • state1.h

    //State1.h
    #ifndef STATE1
    #define STATE1
    
    #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 "pkt1.h" //comment this line to resolve the compliation error
    
    namespace msm = boost::msm;
    namespace msmf = boost::msm::front;
    namespace mpl = boost::mpl;
    
    struct State1:msmf::state<>
    {
        // Entry action
        template <class Event,class Fsm>
        void on_entry(Event const&, Fsm& ) const {
            std::cout << "State1::on_entry()" << std::endl;
            Pkt1 pkt; pkt.send();//comment this line to resolve the compliation error
        }
        // Exit action
        template <class Event,class Fsm>
        void on_exit(Event const&, Fsm&) const {
            std::cout << "State1::on_exit()" << std::endl;
        }
    };
    
    #endif // STATE1
    
  • state2.h

    //State2.h
    #ifndef STATE2
    #define STATE2
    
    #include <iostream>
    #include <boost/msm/back/state_machine.hpp>
    #include <boost/msm/front/state_machine_def.hpp>
    #include <boost/msm/front/functor_row.hpp>
    
    namespace msm = boost::msm;
    namespace msmf = boost::msm::front;
    namespace mpl = boost::mpl;
    struct State2:msmf::state<> 
    {
       // Entry action
       template <class Event,class Fsm>
       void on_entry(Event const&, Fsm&) const {
       std::cout << "State2::on_entry()" << std::endl;
       }
       // Exit action
       template <class Event,class Fsm>
       void on_exit(Event const&, Fsm&) const {
            std::cout << "State2::on_exit()" << std::endl;
       }
    };
    
    #endif // STATE2
    
4

1 回答 1

2

1) How should I resolve this circular dependency?

2) I think the way forward would be to add an implementation file (.cpp) for my Pkt1 class and transfer the #include "myfsm.h" to this file, thus breaking the circular dependency. But how can I forward declare the MyFsm in the header file?

Correct. In Pkt1.h you would forward declare MyFsm, but it's just a typedef to some templated boost type. The easiest way here is to duplicate the typedef (or using) while forward-declaring the class you are using as template parameter:

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

struct MyFsm_;
using MyFsm = boost::msm::back::state_machine<MyFsm_>;

(If you use this part multiple times, you should probably put it into a header to avoid code duplication).

Then move all function implementations into Pkt1.cpp while keeping the declarations in the header. This works because (or as long as) all your functions in there only take pointers or references to MyFsm, because the compiler doesn't need to know more than "it's a pointer" at that point.

Pkt1.h:

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

struct MyFsm_;
using MyFsm = boost::msm::back::state_machine<MyFsm_>;


class Pkt1
{
public:
    Pkt1() {}

    void dispatch(MyFsm *fsm);

    void send();
};

Pkt1.cpp:

#include "pkt1.h"

#include "myfsm.h"
#include "events.h"

#include <iostream>

void Pkt1::dispatch(MyFsm *fsm)
{
    fsm->process_event(Event1());
}

void Pkt1::send()
{
    std::cout<<"pkt1 sent out ..."<<std::endl;
}

Demo: https://wandbox.org/permlink/5zMsbolOMPN0biaY

3) I am new to boost::msm/CRTP and the code is confusing to me. How can State1 get access to MyFsm while I have not included the corresponding header to state1.h?? (maybe because MyFsm derives from the functor front/back end which its header is included and allows virtual functions to call the corresponding MyFsm functions!!??)

The key here is that on_entry and on_exit are template functions. The code for them is only generated when they are used - such as within the FSM implementation (which is inside boost, we can't see it here). That is why they must be in the header: The full function bodies must be visible to the compiler when it instantiates (i.e. generates code for an instance of) the function templates. At that point, the template argument Fsm is substituted for MyFsm (and one of your events for Event) so everything is known and works out.

I would recommend reading up on Translation Units and how C/C++ compilers generate code (i.e. what happens with your .h and .cpp files). Once you understand that, a lot of things should fall into place.

于 2018-06-04T12:19:55.243 回答