Как использовать прямое объявление с boost :: msm, чтобы избежать циклической зависимости? - PullRequest
0 голосов
/ 04 июня 2018

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

0 голосов
/ 04 июня 2018

1) Как мне разрешить эту циклическую зависимость?

2) Я думаю, что дальнейшим шагом было бы добавить файл реализации (.cpp) для моего класса Pkt1 и передать #include "myfsm.h "к этому файлу, тем самым нарушая круговую зависимость.Но как мне переслать объявление MyFsm в заголовочном файле?

Правильно.В Pkt1.h вы бы объявляли MyFsm, но это просто определение типа для некоторого шаблонного типа повышения.Самым простым способом здесь является дублирование 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 (которая находится внутри boost, мы не можем видеть это здесь).Вот почему они должны быть в заголовке: полные тела функций должны быть видны компилятору, когда он создает (то есть генерирует код для экземпляра) шаблоны функций.В этот момент аргумент шаблона Fsm заменяется на MyFsm (и одно из ваших событий на Event), так что все известно и работает.

Я бы порекомендовал прочитать о единицах перевода икак компиляторы C / C ++ генерируют код (то есть, что происходит с вашими .h и .cpp файлами).Как только вы это поймете, многое должно встать на свои места.

...