Program Listing for File KPStateMachine.hpp

Return to documentation for file (src/KPStateMachine.hpp)

#pragma once
#include <KPSubject.hpp>
#include <KPStateMachineObserver.hpp>
#include <unordered_map>

class KPState;
class KPStateMachine : public KPComponent, public KPSubject<KPStateMachineObserver> {
private:
    using Middleware = std::function<void(int)>;
    using StateName  = const char *;
    std::unordered_map<StateName, KPState *> mapNameToState;
    std::unordered_map<StateName, Middleware> mapNameToMiddleware;
    KPState * currentState = nullptr;

public:
    using KPComponent::KPComponent;

    template <typename T>
    void registerState(T && state, StateName name, Middleware middleware = nullptr) {
        if (mapNameToState.count(name)) {
            halt(TRACE, name, " is already used");
        }

        if (name == nullptr) {
            halt(TRACE, "State must have a name");
        }

        T * copy             = new T{std::forward<T>(state)};
        copy->name           = name;
        mapNameToState[name] = copy;
        if (middleware) {
            mapNameToMiddleware[name] = middleware;
        } else {
            mapNameToMiddleware[name] = [name](int code) {
                halt(TRACE, "Unhandled state transition: ", name);
            };
        }
    }

    template <typename T>
    void registerState(T && state, StateName name, StateName next) {
        registerState(std::forward<T>(state), name, [this, next](int code) { transitionTo(next); });
    }

    template <typename T = KPState>
    T & getState(StateName name) {
        auto c = mapNameToState.find(name);
        if (c != mapNameToState.end()) {
            return *static_cast<T *>(c->second);
        } else {
            halt(TRACE, "Unregistered state name: ", name);
        }
    }

    KPState * getCurrentState() const {
        return currentState;
    }

    void next(int code = 0) const;

    void restart();

    void transitionTo(StateName name);

protected:
    void setup() override;
    void update() override;
};