c++boostboost-msm

Boost MSM with constructor parameters on a submachine


I'm trying to share the SyncBox object between a main state-machine and a sub state-machine. The ideal way is to pass it on the constructor (sub state machine will be a initial state of one of the regions in a more complex case). No matter what, I can't make this code compile and execute properly. Any help?

#include <iostream>
#include <mutex>
#include <boost/msm/back/state_machine.hpp>
#include <boost/msm/front/state_machine_def.hpp>
#include <boost/msm/front/functor_row.hpp>
#include <boost/msm/front/euml/common.hpp>

namespace msm = boost::msm;
namespace mpl = boost::mpl;
using namespace boost::msm::front;

class SyncBox {
    std::mutex mtx;
};

struct SubSMFE : public msm::front::state_machine_def<SubSMFE>
{
    struct InitState : public msm::front::state<> {};
    struct FakeState : public msm::front::state<> {};
    typedef InitState initial_state;
    struct transition_table : mpl::vector<
        Row < InitState, none, FakeState>
    > {};
};

//typedef msm::back::state_machine<SubSMFE> SubSM;
class SubSM : public msm::back::state_machine<SubSMFE> {
private:
    SyncBox& sb;
public:
    SubSM(SyncBox& sb) : sb(sb) { std::cout << "SubSMFE constructor" << std::endl; };
    void oneFunction() {
        // here i use syncBox. it must be a function of SubSM, not SubSMFE (oneFunction overrides start or enqueue_event)
    };
};

struct mainSMFE : public msm::front::state_machine_def<mainSMFE>
{
protected:
    SyncBox sb;
public:
    struct InitState : public msm::front::state<> {};
    typedef InitState initial_state;
    struct transition_table : mpl::vector<
        Row < InitState, none, SubSM>
    > {};
};

class mainSM : public msm::back::state_machine<mainSMFE> {
public:
    mainSM() : msm::back::state_machine<mainSMFE>(msm::back::states_ << SubSM(sb)) { };
};

int main()
{
    mainSM sm;
    return 0;
}

Solution

  • As far as I can tell you had everything correctly.

    The problem, however, is that the substate-list requires all state elements to be default-constructible regardless. (This might not be true for the frontend class, as far as I can tell, but haven't looked all the way into it).

    This means that storing a reference as member is off-limits. What I'd suggest instead is storing a pointer.

    class SubSM : public msm::back::state_machine<SubSMFE> {
      private:
        SyncBox* psb = nullptr;
    
      public:
        SubSM() = default;
    
        SubSM(SyncBox& psb)
            : psb(&psb)
        {
            std::cout << "SubSMFE constructor" << std::endl;
        };
    

    It does work as expected:

    Live On Coliru

    #include <boost/msm/back/state_machine.hpp>
    //#include <boost/msm/front/euml/common.hpp>
    #include <boost/msm/front/functor_row.hpp>
    #include <boost/msm/front/state_machine_def.hpp>
    #include <iostream>
    #include <mutex>
    
    namespace msm = boost::msm;
    namespace mpl = boost::mpl;
    using namespace boost::msm::front;
    
    class SyncBox {
        std::mutex mtx;
    };
    
    struct SubSMFE : public msm::front::state_machine_def<SubSMFE> {
        struct InitState : public msm::front::state<> { };
        struct FakeState : public msm::front::state<> { };
        using initial_state = InitState;
        // clang-format off
        struct transition_table : mpl::vector<
            Row < InitState, none, FakeState>
        > {};
        // clang-format on
    };
    
    // typedef msm::back::state_machine<SubSMFE> SubSM;
    
    class SubSM : public msm::back::state_machine<SubSMFE> {
      private:
        SyncBox* psb = nullptr;
    
      public:
        SubSM() = default;
    
        explicit SubSM(SyncBox& psb)
            : psb(&psb)
        {
            std::cout << "SubSMFE constructor" << std::endl;
        };
    
        void oneFunction() {
            // here i use syncBox. it must be a function of SubSM, not SubSMFE
            // (oneFunction overrides start or enqueue_event)
        };
    };
    
    class mainSMFE : public msm::front::state_machine_def<mainSMFE> {
      protected:
        SyncBox sb;
    
      public:
        struct InitState : public msm::front::state<> { };
        using initial_state = InitState;
        // clang-format off
        struct transition_table : mpl::vector<
            Row < InitState, none, SubSM >
        > {};
        // clang-format on
    };
    
    class mainSM : public msm::back::state_machine<mainSMFE> {
      public:
        mainSM()
            : msm::back::state_machine<mainSMFE>(
                msm::back::states_ << SubSM(sb)) {};
    };
    
    int main() {
        mainSM sm;
    }
    

    Prints

    SubSMFE constructor