如何使用boost :: msm进行前向声明以避免循环依赖?

时间:2018-06-04 10:55:32

标签: c++ boost template-meta-programming code-separation boost-msm

我正在尝试使用boost :: msm实现一个简单的协议。当数据包到达时,它们将被处理并分派到状态机(SM)以进行相应的处理。

我的pkt类(即Pkt1)需要fsm的句柄,允许它调用fsm->process_event(...)(当然我会将#include "myfsm.h"添加到pkt1.h的顶部。) / p>

到目前为止一切顺利。但是,如果我的状态机(比如State1)需要通过发送数据包来响应该数据包呢?现在我将“pkt1.h”标题包含在“state1.h”的顶部,这样我就可以创建Pkt1的实例并调用它的send()函数。

好吧,你可能会猜到这最后的包含会导致“循环依赖”

可以找到示例代码(带错误):https://wandbox.org/permlink/IlFsUQyLPLrLl2RW(我第一次使用wandbox,希望一切正常)

注意)在“state1.h”文件中删除#include "pkt1.h"& on_entry(..)... Pkt1 pkt; pkt.send();使其可编辑。

  

问题:

1)我该如何解决这种循环依赖?

2)我认为前进的方法是为我的Pkt1类添加一个实现文件(.cpp)并将#include "myfsm.h"传递给该文件,从而打破循环依赖。但是我如何转发在头文件中声明MyFsm

3)我是boost :: msm / CRTP的新手,代码让我很困惑。如果我没有将相应的头部包含到state1.h中,State1如何访问MyFsm? (也许是因为MyFsm派生出了包含其标题的仿函数前/后端,并允许虚函数调用相应的MyFsm函数!! ??)

非常感谢您的时间和提前帮助。

包含的代码:

  • 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
    

1 个答案:

答案 0 :(得分:2)

  

1)我该如何解决这种循环依赖?

     

2)我认为前进的方法是为我的Pkt1类添加一个实现文件(.cpp)并传输#include&#34; myfsm.h&#34;到这个文件,从而打破了循环依赖。但是如何在头文件中转发声明MyFsm?

正确。在Pkt1.h中,您会转发声明MyFsm,但它只是某个模板化提升类型的typedef。这里最简单的方法是在向前声明要用作模板参数的类时复制typedef(或使用):

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

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

(如果您多次使用此部分,您应该将其放入标题中以避免代码重复。)

然后将所有函数实现移动到Pkt1.cpp,同时保持标头中的声明。这是因为(或者只要)所有函数都只有指针或引用MyFsm,因为编译器不需要知道超过&#34;它是指针&#34; 34;那时候。

<强> 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;
}

演示:https://wandbox.org/permlink/5zMsbolOMPN0biaY

  

3)我是boost :: msm / CRTP的新手,代码让我很困惑。如果没有将相应的头部包含到state1.h中,State1如何访问MyFsm? (也许是因为MyFsm派生于其包含头部的仿函数前/后端,并允许虚函数调用相应的MyFsm函数!! ??)

此处的关键是on_entryon_exit模板功能。它们的代码仅在使用时生成 - 例如在FSM实现中(在内部提升,我们无法在此处看到它)。这就是为什么它们必须在标题中:当编译器实例化(即生成函数模板的实例的代码)时,编译器必须可以看到完整的函数体。此时,模板参数Fsm将替换MyFsm(以及Event的一个事件),因此一切都已知并且可以解决。

我建议您阅读翻译单元以及C / C ++编译器如何生成代码(即您的.h.cpp文件会发生什么)。一旦你理解了这一点,就应该落实很多东西。