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

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

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

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

到目前为止一切顺利。但是,如果我的状态机(比如 State1)需要通过自己发送数据包来对该数据包做出反应怎么办?现在我将 "pkt1.h" header 包含到 "state1.h" 的顶部,这样我就可以创建 Pkt1 的实例并调用它的 send() 函数。

正如您可能猜到的那样,最终包含会导致 "circular dependency"

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

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

Questions:

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

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

3) 我是 boost::msm/CRTP 的新手,代码让我感到困惑。 State1 如何访问 MyFsm 而我还没有包含相应的 header 到 state1.h? (可能是因为 MyFsm 派生自函子 front/back 端,其中包含其 header 并允许虚函数调用相应的 MyFsm 函数!!??)

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

包含代码:

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?

正确。在 Pkt1.h 中,您将转发声明 MyFsm,但它只是某些模板化增强类型的 typedef。这里最简单的方法是复制 typedef(或使用),而 forward-declaring 您用作模板参数的 class:

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

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

(如果你多次使用这部分,你可能应该把它放在一个header中以避免代码重复)。

然后将所有函数实现移动到 Pkt1.cpp 中,同时将声明保留在 header 中。这是可行的,因为(或只要)你在那里的所有函数只接受指向 MyFsm 的指针或引用,因为编译器在那个时候不需要知道超过 "it's a pointer"。

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) 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!!??)

这里的关键是on_entryon_exit模板函数。它们的代码仅在 使用 时生成 - 例如在 FSM 实现中(在 boost 中,我们在这里看不到)。这就是为什么它们必须在 header 中:当编译器 实例化 (即为函数模板的实例生成代码)时,编译器必须可以看到完整的函数体。在这一点上,模板参数 Fsm 被替换为 MyFsm(以及 Event 的你的事件之一)所以一切都是已知的并且有效。

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