如何使用 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 函数!!??)
非常感谢您的宝贵时间和提前帮助。
包含代码:
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) 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_entry
和on_exit
是模板函数。它们的代码仅在 使用 时生成 - 例如在 FSM 实现中(在 boost 中,我们在这里看不到)。这就是为什么它们必须在 header 中:当编译器 实例化 (即为函数模板的实例生成代码)时,编译器必须可以看到完整的函数体。在这一点上,模板参数 Fsm
被替换为 MyFsm
(以及 Event
的你的事件之一)所以一切都是已知的并且有效。
我建议阅读翻译单元以及 C/C++ 编译器如何生成代码(即你的 .h
和 .cpp
文件会发生什么)。一旦你明白了这一点,很多事情就会水到渠成。
我正在尝试使用 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 函数!!??)
非常感谢您的宝贵时间和提前帮助。
包含代码:
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) 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_entry
和on_exit
是模板函数。它们的代码仅在 使用 时生成 - 例如在 FSM 实现中(在 boost 中,我们在这里看不到)。这就是为什么它们必须在 header 中:当编译器 实例化 (即为函数模板的实例生成代码)时,编译器必须可以看到完整的函数体。在这一点上,模板参数 Fsm
被替换为 MyFsm
(以及 Event
的你的事件之一)所以一切都是已知的并且有效。
我建议阅读翻译单元以及 C/C++ 编译器如何生成代码(即你的 .h
和 .cpp
文件会发生什么)。一旦你明白了这一点,很多事情就会水到渠成。