简体   繁体   English

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

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

I am trying to implement a simple protocol with boost::msm. 我正在尝试使用boost :: msm实现一个简单的协议。 As packets arrive they are processed and dispatched to the State Machine (SM) to be handled accordingly. 当数据包到达时,将对它们进行处理并将其分派到状态机(SM),以进行相应的处理。

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

So far so good. 到现在为止还挺好。 But what if my state machine (say State1) requires to react to that packet by sending a packet itself? 但是,如果我的状态机(例如State1)需要通过发送数据包本身来对该数据包做出反应怎么办? 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. 现在,我将在“ state1.h”的顶部包括“ pkt1.h”标头,以便我可以创建Pkt1的实例并调用其send()函数。

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

Note) In the "state1.h" file remove the #include "pkt1.h" & on_entry(..)... Pkt1 pkt; pkt.send(); 注意)在“ state1.h”文件中,删除#include "pkt1.h"on_entry(..)... Pkt1 pkt; pkt.send(); on_entry(..)... Pkt1 pkt; pkt.send(); to make it compilable. 使它可编译。

Questions: 问题:

1) How should I resolve this circular dependency? 1)我应该如何解决这种循环依赖关系?

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. 2)我认为前进的方法是为我的Pkt1类添加一个实现文件(.cpp),并将#include "myfsm.h"传输到此文件,从而打破循环依赖关系。 But how can I forward declare the MyFsm in the header file? 但是,如何在头文件中转发声明MyFsm

3) I am new to boost::msm/CRTP and the code is confusing to me. 3)我不熟悉boost :: msm / CRTP,代码对我来说很混乱。 How can State1 get access to MyFsm while I have not included the corresponding header to state1.h?? 当我没有在state1.h中包含相应的头文件时,State1如何获得对MyFsm访问权限? (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!!??) (也许是因为MyFsm从函子的前端/后端派生而来,该MyFsm的头包含在其中,并允许虚函数调用相应的MyFsm函数!!!)

Many thanks for your time and help in advance. 非常感谢您的宝贵时间和事先的帮助。

Code Included: 包含的代码:

  • events.h events.h

     #ifndef EVENTS #define EVENTS // ----- Events struct Event1 {}; struct Event2 {}; #endif // EVENTS 
  • main.cpp 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

     //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 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 状态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 
  • state2.h 状态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) How should I resolve this circular dependency? 1)我应该如何解决这种循环依赖关系?

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. 2)我认为前进的方法是为我的Pkt1类添加一个实现文件(.cpp),并将#include“ myfsm.h”传输到此文件,从而打破循环依赖关系。 But how can I forward declare the MyFsm in the header file? 但是,如何在头文件中转发声明MyFsm?

Correct. 正确。 In Pkt1.h you would forward declare MyFsm , but it's just a typedef to some templated boost type. Pkt1.h您将向前声明MyFsm ,但这只是一些模板化boost类型的typedef。 The easiest way here is to duplicate the typedef (or using) while forward-declaring the class you are using as template parameter: 这里最简单的方法是在向前声明要用作模板参数的类时复制typedef(或使用):

#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. 然后将所有函数实现都移到Pkt1.cpp同时将声明保留在标头中。 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. 之所以MyFsm ,是因为(或只要您在那里的所有函数)仅使用指向MyFsm指针或引用,这是因为编译器此时不需要了解的只是“它是指针”。

Pkt1.h : 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 : 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 演示: https : //wandbox.org/permlink/5zMsbolOMPN0biaY

3) I am new to boost::msm/CRTP and the code is confusing to me. 3)我不熟悉boost :: msm / CRTP,代码对我来说很混乱。 How can State1 get access to MyFsm while I have not included the corresponding header to state1.h?? 当我没有在state1.h中包含相应的头文件时,State1如何获得对MyFsm的访问权限? (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!!??) (也许是因为MyFsm从函子的前端/后端派生而来,该函件的头包含在其中,并允许虚函数调用相应的MyFsm函数!!!)

The key here is that on_entry and on_exit are template functions. 这里的关键是on_entryon_exit模板函数。 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). 仅在使用它们时才生成它们的代码-例如在FSM实现中(在boost内部,我们在这里看不到)。 That is why they must be in the header: The full function bodies must be visible to the compiler when it instantiates (ie 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. 那时,模板参数Fsm代替了MyFsm (以及Event ),因此一切都已知并可以解决。

I would recommend reading up on Translation Units and how C/C++ compilers generate code (ie what happens with your .h and .cpp files). 我建议您阅读翻译单元以及C / C ++编译器如何生成代码(即.h.cpp文件会发生什么)。 Once you understand that, a lot of things should fall into place. 一旦您了解了这一点,很多事情就应该解决。

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

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