繁体   English   中英

如何使用带有boost :: msm的前向声明来避免循环依赖?

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

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

我的pkt类(即Pkt1)需要fsm的句柄,以使其能够调用fsm->process_event(...) (当然,我会在#include "myfsm.h"的顶部添加#include "myfsm.h" )。

到现在为止还挺好。 但是,如果我的状态机(例如State1)需要通过发送数据包本身来对该数据包做出反应怎么办? 现在,我将在“ state1.h”的顶部包括“ pkt1.h”标头,以便我可以创建Pkt1的实例并调用其send()函数。

您可能会猜到,最后的包含会导致“循环依赖”

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

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

问题:

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

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

3)我不熟悉boost :: msm / CRTP,代码对我来说很混乱。 当我没有在state1.h中包含相应的头文件时,State1如何获得对MyFsm访问权限? (也许是因为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 
  • 状态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 ,但这只是一些模板化boost类型的typedef。 这里最简单的方法是在向前声明要用作模板参数的类时复制typedef(或使用):

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

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

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

然后将所有函数实现都移到Pkt1.cpp同时将声明保留在标头中。 之所以MyFsm ,是因为(或只要您在那里的所有函数)仅使用指向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,代码对我来说很混乱。 当我没有在state1.h中包含相应的头文件时,State1如何获得对MyFsm的访问权限? (也许是因为MyFsm从函子的前端/后端派生而来,该函件的头包含在其中,并允许虚函数调用相应的MyFsm函数!!!)

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM