c++booststate-machineboost-sml

Boost.SML - Violation of memory protection/Segmentation fault


The program returns:

Segmentation fault (core dumped)

What is wrong?

The implementation is based on the example - 'Nested' form documentation BOOST.SML:

http://boost-experimental.github.io/sml/examples/index.html#nested

SM is used to call methods from the Server. The Server is used to switch states in the SM.

#include <boost/sml.hpp>
#include <iostream>
namespace sml = boost::sml;
using namespace std;

template <class = class Dummy>
class Server
{
struct pollEvent{};
struct msgRcdEvent{};

class States
{
public:
 States(Server *server): _server(server){}

auto operator()() const noexcept
{
  auto msgRcdAction = [this] {
  std::cout << "HB server -> msgRcdAction " << std::endl;
 _server->recvMsg();
};

auto pollAction = [this] {
 std::cout << "HB server -> pollAction " << std::endl;
  _server->poll();
};

using namespace sml;

return make_transition_table(
 *"idle"_s + event<pollEvent> / pollAction    = "Poll"_s,
 "Poll"_s + event<msgRcdEvent> / msgRcdAction = "RcdMsg"_s,
 "RcdMsg"_s + event<pollEvent> / pollAction   = "Poll"_s
);
}
private:
 Server *_server{nullptr};
};

public:
 Server()
 {
  _states = new States(this);
  _sm     = new sml::sm<States>(*_states);
 }

 void process(){_sm->process_event(pollEvent{});}
 void poll(){_sm->process_event(msgRcdEvent{});}
 void recvMsg(){_sm->process_event(pollEvent{});}

private:
 States *_states{nullptr};
 sml::sm<States> *_sm{nullptr};
};

int main()
{
 Server<> s{};
 s.process();
 return 0;
}

Solution

  • Segmentation fault is caused by stack overflow. Because _sm->process_event(...) is called recursively. The function processes the event immediately.

    In order to avoid it, we need something that queuing event mechanism instead of process the event immediately.

    sml provides it.

    Here is the code. Please see comments 1 to 3.

    #include <boost/sml.hpp>
    #include <iostream>
    #include <queue>
    
    namespace sml = boost::sml;
    using namespace std;
    
    template <class = class Dummy>
    class Server
    {
        struct pollEvent{};
        struct msgRcdEvent{};
    
        class States
        {
        public:
            States(Server *server): _server(server){}
    
            auto operator()() const noexcept
            {
                auto msgRcdAction =
                    // 2. Add parameters to lambda expression
                    //    The second parameter is process callable object that is from
                    //    action. The template argument `pollEvent` is the event you want
                    //    to pass to the `process`.
                    //    You can write multiple template arguments.
                    //    e.g.) sml::back::process<pollEvent, msgRcdEvent>
                    [this] (auto const& /*ev*/, sml::back::process<pollEvent> process) {
                        std::cout << "HB server -> msgRcdAction " << std::endl;
                        _server->recvMsg(process);
                    };
    
                auto pollAction =
                    [this] (auto const& /*ev*/, sml::back::process<msgRcdEvent> process) {
                        std::cout << "HB server -> pollAction " << std::endl;
                        _server->poll(process);
                    };
    
                using namespace sml;
    
                return make_transition_table(
                    *"idle"_s + event<pollEvent> / pollAction    = "Poll"_s,
                    "Poll"_s + event<msgRcdEvent> / msgRcdAction = "RcdMsg"_s,
                    "RcdMsg"_s + event<pollEvent> / pollAction   = "Poll"_s
                );
            }
        private:
            Server *_server{nullptr};
        };
    
    public:
        Server()
        {
            _states = new States(this);
            _sm     = new sml::sm<States, sml::process_queue<std::queue>>(*_states);
        }
    
        void process1(){_sm->process_event(pollEvent{});}
    
        // 3. Invoke process callable object
        template <typename Process>
        void poll(Process p){ p(msgRcdEvent{});}
        template <typename Process>
        void recvMsg(Process p){ p(pollEvent{});}
    
    private:
        States *_states{nullptr};
    
        // 1. Add `sml::process_queue<std::queue>` template argument to `sml::sm`.
        sml::sm<States, sml::process_queue<std::queue>> *_sm{nullptr};
    };
    
    int main()
    {
        Server<> s{};
        s.process1();
        return 0;
    }
    

    sml::back::process<pollEvent> (comment 2) is a callable (function like) object that is queuing the event. You can call it instead of _sm->process_event(...) (comment 3). The queue is set as the template argument of sm (comment 1).