Я пытаюсь реализовать простой протокол с boost :: msm. По мере поступления пакетов они обрабатываются и отправляются в конечный автомат (SM) для соответствующей обработки.
Мой класс pkt (т.е. Pkt1) требует дескриптор для fsm, который позволил бы ему вызывать fsm->process_event(...)
(и конечно я бы добавил #include "myfsm.h"
к вершине pkt1.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) Я думаю, что дальнейшим шагом было бы добавить файл реализации (.cpp) для моего класса Pkt1 и передать #include "myfsm.h"
в этот файл, тем самым нарушая круговую зависимость. Но как я могу объявить MyFsm
в заголовочном файле?
3) Я новичок в boost :: msm / CRTP и код сбивает меня с толку. Как State1 может получить доступ к MyFsm
пока я не включил соответствующий заголовок в state1.h ?? (Может быть, потому, что 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
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) Как мне разрешить эту круговую зависимость?
2) Я думаю, что дальнейшим шагом было бы добавить файл реализации (.cpp) для моего класса Pkt1 и перенести в этот файл файл #include «myfsm.h», тем самым нарушив циклическую зависимость. Но как я могу отправить объявление MyFsm в заголовочный файл?
Правильный. В Pkt1.h
Вы бы вперед объявить MyFsm
, но это просто typedef для некоторого шаблонного типа boost. Самый простой способ здесь — дублировать 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 и код сбивает меня с толку. Как State1 может получить доступ к MyFsm, пока я не включил соответствующий заголовок в state1.h ?? (возможно, потому что MyFsm происходит от внешнего / внутреннего конца функтора, в который включен его заголовок, и позволяет виртуальным функциям вызывать соответствующие функции MyFsm !! ??)
Ключевым моментом здесь является то, что on_entry
а также on_exit
являются шаблон функции. Код для них генерируется только тогда, когда они используемый — например, в рамках реализации FSM (которая находится внутри поддержки, мы не можем видеть это здесь). Вот почему они должны быть в заголовке: полное тело функции должно быть видно компилятору, когда он конкретизирует (т.е. генерирует код для экземпляра) шаблонов функций. В этот момент аргумент шаблона Fsm
заменяется MyFsm
(и одно из ваших мероприятий для Event
) так что все известно и работает.
Я бы порекомендовал прочитать о единицах перевода и о том, как компиляторы C / C ++ генерируют код (то есть, что происходит с вашим .h
а также .cpp
файлы). Как только вы это поймете, многое должно стать на свои места.
Других решений пока нет …