How to use forward declaration with boost::msm to avoid circular dependency?

qwa

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
    
Max Langhof

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.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Circular dependency - how to resolve and avoid

How to avoid circular dependency in this project?

How to refactor this to avoid a circular dependency?

Will forward-declaration of two classes lead to circular dependency in the constructor?

How can I avoid forward declaration error?

How to use forward declaration in C?

How to avoid circular dependency with Dagger 2?

Vuerouter : need to import, but how to avoid circular dependency?

How to avoid circular dependency in multiple API healthchecks?

Avoid Circular Dependency injection

avoid circular dependency on hibernate

What is the use case of boost::hana forward declaration headers?

circular dependency (class on typedef, typedef on class), forward declaration gives ambigous call?

c++ "Incomplete type not allowed" error accessing class reference information (Circular dependency with forward declaration)

How to avoid a circular dependency between a GUI and the rest of a program?

How to avoid circular dependency between modules when working with recursive functions?

How to avoid circular dependency while adding references to windows runtimecomponent?

DAX-Power BI: How to avoid circular dependency in calculated column

How to avoid duplication of dependency declaration in a Clojure/ClojureScript project?

Circular dependency c++ fails with forward decleration

Can I avoid nested namespaces in forward declaration?

Forward declaration of struct in C how-to and use-case

How use Forward declaration in clojure and unbound variables across namespace

How not to get a circular dependency?

How to solve the circular dependency

How to use a mono in another mono that have circular dependency

How to avoid circular imports?

How to avoid this circular import?

use of undefined type in circular dependency