如何将前向声明与 boost::msm 一起使用以避免循环依赖?



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

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

目前为止,一切都好。但是,如果我的状态机(比如 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 函数!??(

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

代码包括:

  • 事件.h

    #ifndef EVENTS
    #define EVENTS
    
    // ----- Events
    struct Event1 {};
    struct Event2 {};
    #endif // EVENTS
    
  • 主.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
    
  • 状态1.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
    
  • 状态2.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(我应该如何解决这种循环依赖关系?

2(我认为前进的方法是为我的Pkt1类添加一个实现文件(.cpp(,并将 #include"myfsm.h"转移到该文件中,从而打破循环依赖。但是如何在头文件中转发声明 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的指针或引用,因为编译器在这一点上不需要知道"它是一个指针"之外的更多。

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的新手,代码让我感到困惑。当我没有将相应的标头包含在状态 1.h 时,状态 1 如何访问 MyFsm?(可能是因为 MyFsm 派生自包含其标头的函子前端/后端,并允许虚拟函数调用相应的 MyFsm 函数!??(

这里的关键是on_entryon_exit模板函数。它们的代码仅在使用它们时生成 - 例如在 FSM 实现中(在 boost 内部,我们在这里看不到它(。这就是为什么它们必须位于标头中:当编译器实例(即为函数模板的实例生成代码(时,完整的函数体必须对编译器可见。此时,模板参数Fsm被替换为MyFsm(以及Event的事件之一(,因此一切都是已知的并解决的。

我建议阅读翻译单元以及C/C++编译器如何生成代码(即.h.cpp文件会发生什么(。一旦你明白了这一点,很多事情都应该到位。

最新更新