Как использовать прямое объявление с boost :: msm, чтобы избежать циклической зависимости?

Я пытаюсь реализовать простой протокол с 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
    

0

Решение

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 файлы). Как только вы это поймете, многое должно стать на свои места.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]