首页 > 解决方案 > 如何将 boost msm 与 boost 信号结合使用?

问题描述

我对“boost msm”真的很陌生,现在我遇到了一个问题,如何在boost msm中使用boost信号,我尝试了很多次但没有用。

我想要实现的是当机器改变状态时,动作操作员发出信号,我的外部实体捕捉到信号并继续执行其他功能,但现在我无法将信号发出机器,我无法连接与外部实体的信号。有没有例子?

标签: c++boostboost-msm

解决方案


You can combine Boost.Signals2 and Boost.MSM.

Here is the simple signals2 example.

https://wandbox.org/permlink/XZzGIIVWXjvOPzdd (Running demo)

#include <iostream>
#include <string>

#include <boost/signals2/signal.hpp>
#include <iostream>

// begin -- signals2 code
struct signal_data {
    int i;
    std::string str;
};

struct sender {
    boost::signals2::signal<void(signal_data const&)> foo;

    void send() {
        foo(signal_data {42, "ABC"} );
    }
};

struct receiver_1 {
    void on_foo(signal_data const& sd) {
        std::cout << __PRETTY_FUNCTION__ << " " << sd.i << ", " << sd.str << std::endl;
    }
};

struct receiver_2 {
    void on_foo(signal_data const& sd) {
        std::cout << __PRETTY_FUNCTION__ << " " << sd.i << ", " << sd.str << std::endl;
    }
};

// end -- signals2 code

int main() {
    // signals setup ----------------------------------------
    // external entities
    receiver_1 r1;
    receiver_2 r2;

    sender s;

    // make connection
    // using lambda expression
    s.foo.connect(
        [&] (signal_data const& param) {
            r1.on_foo(param);
        }
    );
    // or bind
    s.foo.connect(std::bind(&receiver_2::on_foo, &r2, std::placeholders::_1));
    s.send();
}

The signal sending point is s.send(). If we can call s.send() from msm's action, the goal would be achieved.

Here is the simple msm example.

https://wandbox.org/permlink/tnRSQ07anNe49GpO (Running demo)

#include <iostream>
#include <string>

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

#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>

// begin -- msm code
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;

// ----- Events
struct event1 {};

// ----- State machine
struct sm1_:msmf::state_machine_def<sm1_> {
    // States
    struct state1:msmf::state<> {};

    // Set initial state
    using initial_state = state1;

    // Actions
    struct action {
        template <class Event, class Fsm, class SourceState, class TargetState>
        void operator()(Event const&, Fsm&, SourceState&, TargetState&) const {
            std::cout << "action()" << std::endl;
            // want to call s.send() here
        }
    };

    // Transition table
    struct transition_table:mpl::vector<
        //          Start   Event   Next        Action      Guard
        msmf::Row < state1, event1, msmf::none, action,     msmf::none >
    > {};
};

// Pick a back-end
using sm1 = msm::back::state_machine<sm1_>;

// end -- msm code

int main() {
    sm1 s1;

    s1.start(); 
    std::cout << "Send event1" << std::endl;
    s1.process_event(event1());
}

The action is defined as follows:

    struct action {
        template <class Event, class Fsm, class SourceState, class TargetState>
        void operator()(Event const&, Fsm&, SourceState&, TargetState&) const {
            std::cout << "action()" << std::endl;
            // want to call s.send() here
        }
    };

How to call s.send() in the action?

First, add sender's reference as a member variable of sm1_.

sender& s; // define sender reference as a member variable

Then, initialize it at the constructor.

sm1_(sender& s):s(s) {} // initialize sender in constructor

Then, pass std::ref wrapped sender when creating the state machine.

sm1 s1(std::ref(s)); // pass sender as reference

Finally, call s.send() in the action. You can access s via Fsm reference as follows:

struct action {
    template <class Event, class Fsm, class SourceState, class TargetState>
    void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const {
        std::cout << "action()" << std::endl;
        f.s.send();
    }
};

Here is whole code:

https://wandbox.org/permlink/gh83EW8eado5iOi8 (Running demo)

#include <iostream>
#include <string>

#include <boost/signals2/signal.hpp>
#include <iostream>
#include <boost/msm/back/state_machine.hpp>

#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>

// begin -- signals2 code
struct signal_data {
    int i;
    std::string str;
};

struct sender {
    boost::signals2::signal<void(signal_data const&)> foo;

    void send() {
        foo(signal_data {42, "ABC"} );
    }
};

struct receiver_1 {
    void on_foo(signal_data const& sd) {
        std::cout << __PRETTY_FUNCTION__ << " " << sd.i << ", " << sd.str << std::endl;
    }
};

struct receiver_2 {
    void on_foo(signal_data const& sd) {
        std::cout << __PRETTY_FUNCTION__ << " " << sd.i << ", " << sd.str << std::endl;
    }
};

// end -- signals2 code

// begin -- msm code
namespace msm = boost::msm;
namespace msmf = boost::msm::front;
namespace mpl = boost::mpl;

// ----- Events
struct event1 {};

// ----- State machine
struct sm1_:msmf::state_machine_def<sm1_> {
    sm1_(sender& s):s(s) {} // initialize sender in constructor

    // States
    struct state1:msmf::state<> {};

    // Set initial state
    using initial_state = state1;

    // Actions
    struct action {
        template <class Event, class Fsm, class SourceState, class TargetState>
        void operator()(Event const&, Fsm& f, SourceState&, TargetState&) const {
            std::cout << "action()" << std::endl;
            f.s.send();
        }
    };

    // Transition table
    struct transition_table:mpl::vector<
        //          Start   Event   Next        Action      Guard
        msmf::Row < state1, event1, msmf::none, action,     msmf::none >
    > {};

    sender& s; // define sender reference as a member variable
};

// Pick a back-end
using sm1 = msm::back::state_machine<sm1_>;

// end -- msm code

int main() {
    // signals setup ----------------------------------------
    // external entities
    receiver_1 r1;
    receiver_2 r2;

    sender s;

    // make connection
    // using lambda expression
    s.foo.connect(
        [&] (signal_data const& param) {
            r1.on_foo(param);
        }
    );
    // or bind
    s.foo.connect(std::bind(&receiver_2::on_foo, &r2, std::placeholders::_1));

    // msm setup and process ---------------------------------
    sm1 s1(std::ref(s)); // pass sender as reference

    s1.start(); 
    std::cout << "Send event1" << std::endl;
    s1.process_event(event1());
}

推荐阅读