Gatonito
Gatonito

Reputation: 2184

How to add/initialize members in an SML state or state machine?

I designed this state machine using boost::sml

struct LooperStateMachine {
    auto operator()() const {
        using namespace sml;
        
        return make_transition_table(
                *"beggining"_s + event<heel> / onRecordFirstLoop.value() = "recording_first_loop"_s,
                "recording_first_loop"_s + event<heel>[is_heel_valid] / onRecordLoops.value() = "recording_other_loops_and_playing"_s,
                "recording_first_loop"_s + event<toe>[is_toe_valid] / onPlayLoops.value() = "playing_loops"_s,
                "playing_loops"_s + event<toe>[is_toe_valid]/onStopPlayingLoops.value() = "stopped"_s,
                "playing_loops"_s + event<heel>[is_heel_valid]/onRecordLoops.value() = "recording_other_loops_and_playing"_s,
                "recording_other_loops_and_playing"_s + event<toe>[is_toe_valid] / onPlayLoops.value() = "playing_loops"_s,
                "stopped"_s + event<toe>[is_toe_valid] / onClearLoops.value() = "beggining"_s,
                "stopped"_s + event<heel>[is_heel_valid] / onSaveLoops.value() = "stopped"_s
        );
        
    }

    std::optional<std::function<void()>> onClearLoops;
    std::optional<std::function<void()>> onSaveLoops;
    std::optional<std::function<void()>> onPlayLoops;
    std::optional<std::function<void()>> onRecordLoops;
    std::optional<std::function<void()>> onRecordFirstLoop;
    std::optional<std::function<void()>> onStopPlayingLoops;
};

However, to use it, the sm auto instantiates it:

int main() {
    using namespace sml;

    sm<LooperStateMachine> sm;
    

So I don't get a chance to set my functions.

How can I set the functions?

Upvotes: 1

Views: 1222

Answers (1)

sehe
sehe

Reputation: 393664

To your literal question you might just provide a default constructor/NSMI. However I get that you want to be able to "dynamically" switch out these action "hooks".

About injecting the function hooks, the docs say:

SML states cannot have data as data is injected directly into guards/actions instead

This gave me the idea to separate your runtime state from the state machine:

Aside: optional functions are redundant, function<> can already be value-less and has a convenient conversion to bool fdr that, just like optional<>.

Live On Wandbox

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

namespace looping {
    struct heel { };
    struct toe { };
    static auto is_heel_valid = []() { return false; };
    static auto is_toe_valid = []() { return false; };

    using DynamicAction = std::function<void()>;
    struct Hooks {
        DynamicAction onClearLoops;
        DynamicAction onSaveLoops;
        DynamicAction onPlayLoops;
        DynamicAction onRecordLoops;
        DynamicAction onRecordFirstLoop;
        DynamicAction onStopPlayingLoops;
    };

    static auto onClearLoops       = [](Hooks &hooks) { if (hooks.onClearLoops)       hooks.onClearLoops();       };
    static auto onSaveLoops        = [](Hooks &hooks) { if (hooks.onSaveLoops)        hooks.onSaveLoops();        };
    static auto onPlayLoops        = [](Hooks &hooks) { if (hooks.onPlayLoops)        hooks.onPlayLoops();        };
    static auto onRecordLoops      = [](Hooks &hooks) { if (hooks.onRecordLoops)      hooks.onRecordLoops();      };
    static auto onRecordFirstLoop  = [](Hooks &hooks) { if (hooks.onRecordFirstLoop)  hooks.onRecordFirstLoop();  };
    static auto onStopPlayingLoops = [](Hooks &hooks) { if (hooks.onStopPlayingLoops) hooks.onStopPlayingLoops(); };

    struct LooperStateMachine {
        auto operator()() const {
            using namespace sml;
            
            return make_transition_table(
                *"beginning"_s + event<heel> / onRecordFirstLoop = "recording_first_loop"_s,
                "recording_first_loop"_s + event<heel>[is_heel_valid] / onRecordLoops = "recording_other_loops_and_playing"_s,
                "recording_first_loop"_s + event<toe>[is_toe_valid] / onPlayLoops = "playing_loops"_s,
                "playing_loops"_s + event<toe>[is_toe_valid]/onStopPlayingLoops = "stopped"_s,
                "playing_loops"_s + event<heel>[is_heel_valid]/onRecordLoops = "recording_other_loops_and_playing"_s,
                "recording_other_loops_and_playing"_s + event<toe>[is_toe_valid] / onPlayLoops = "playing_loops"_s,
                "stopped"_s + event<toe>[is_toe_valid] / onClearLoops = "beginning"_s,
                "stopped"_s + event<heel>[is_heel_valid] / onSaveLoops = "stopped"_s
            );
        }
    };
}

int main() {
    looping::Hooks hooks;
    hooks.onClearLoops = [] { std::cout << "Clearing them\n"; };

    sml::sm<looping::LooperStateMachine> sm(hooks);
}

Disclaimer: I have only the flimsiest understanding of this library. I find it to be a mind bender to get to grips with, but in an increasingly positive way.

Upvotes: 3

Related Questions