-
Notifications
You must be signed in to change notification settings - Fork 34
/
app_state_manager.hpp
130 lines (107 loc) · 3.53 KB
/
app_state_manager.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/**
* Copyright Quadrivium LLC
* All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <cstdint>
#include <functional>
#include <stdexcept>
#include <string>
namespace kagome::application {
// concepts that check if an object has a method that is called by app state
// manager. Deliberately avoid checking that the method returns bool,
// because if there's a method with an appropriate name, and it doesn't return
// bool, we want it to be a compile error instead of silently ignoring it
// because the concept is not satisfied.
template <typename T>
concept AppStatePreparable = requires(T &t) { t.prepare(); };
template <typename T>
concept AppStateStartable = requires(T &t) { t.start(); };
template <typename T>
concept AppStateStoppable = requires(T &t) { t.stop(); };
// if an object is registered with AppStateManager but has no method
// that is called by AppStateManager, there's probably something wrong
template <typename T>
concept AppStateControllable =
AppStatePreparable<T> || AppStateStoppable<T> || AppStateStartable<T>;
template <typename T>
concept ActionRetBool = std::same_as<std::invoke_result_t<T>, bool>;
template <typename T>
concept ActionRetVoid = std::is_void_v<std::invoke_result_t<T>>;
class Action {
public:
Action(ActionRetBool auto &&f)
: f_([f = std::move(f)]() mutable { return f(); }) {}
Action(ActionRetVoid auto &&f)
: f_([f = std::move(f)]() mutable { return f(), true; }) {}
bool operator()() {
return f_();
}
private:
std::function<bool()> f_;
};
class AppStateManager {
public:
using OnPrepare = Action;
using OnLaunch = Action;
using OnShutdown = Action;
enum class State : uint8_t {
Init,
Prepare,
ReadyToStart,
Starting,
Works,
ShuttingDown,
ReadyToStop,
};
virtual ~AppStateManager() = default;
/**
* @brief Execute \param cb at stage 'preparations' of application
* @param cb
*/
virtual void atPrepare(OnPrepare &&cb) = 0;
/**
* @brief Execute \param cb immediately before start application
* @param cb
*/
virtual void atLaunch(OnLaunch &&cb) = 0;
/**
* @brief Execute \param cb at stage of shutting down application
* @param cb
*/
virtual void atShutdown(OnShutdown &&cb) = 0;
public:
/**
* @brief Registration special methods (if any) of object as handlers
* for stages of application life-cycle
* @param entity is registered entity
*/
template <AppStateControllable Controlled>
void takeControl(Controlled &entity) {
if constexpr (AppStatePreparable<Controlled>) {
atPrepare([&entity] { return entity.prepare(); });
}
if constexpr (AppStateStartable<Controlled>) {
atLaunch([&entity] { return entity.start(); });
}
if constexpr (AppStateStoppable<Controlled>) {
atShutdown([&entity] { return entity.stop(); });
}
}
/// Start application life cycle
virtual void run() = 0;
/// Initiate shutting down (at any time)
virtual void shutdown() = 0;
/// Get current stage
virtual State state() const = 0;
protected:
virtual void doPrepare() = 0;
virtual void doLaunch() = 0;
virtual void doShutdown() = 0;
};
struct AppStateException : public std::runtime_error {
explicit AppStateException(std::string message)
: std::runtime_error("Wrong workflow at " + std::move(message)) {}
};
} // namespace kagome::application