SAT Solver Template
Loading...
Searching...
No Matches
SubscribableEvent.hpp
Go to the documentation of this file.
1/**
2 * @author Tim Luchterhand
3 * @date 16.11.22
4 * @file SubscribableEvent.hpp
5 * @brief Contains subscribable event class for easy managing of solver events
6 */
7
8#ifndef SUBSCRIBABLEEVENT_HPP
9#define SUBSCRIBABLEEVENT_HPP
10
11#include <functional>
12#include <vector>
13#include <ranges>
14#include <memory>
15#include <algorithm>
16
17namespace sat {
18
19 /**
20 * @brief Identifies a function handler subscribed to a SubscribableEvent. Can be used to unsubscribe the function
21 * handler.
22 * @details @copybrief
23 * This handle automatically unsubscribes the associated event handler at destruction. It avoids undefined behaviour
24 * if the event is already destroyed. However, this mechanism is not thread-safe!
25 */
27 template<typename ...Args>
28 friend class SubscribableEvent;
29 struct Token{};
30 public:
31 SubscriberHandle(const SubscriberHandle &) = default;
32 SubscriberHandle &operator=(const SubscriberHandle &) = default;
33 SubscriberHandle(SubscriberHandle &&other) noexcept = default;
34 SubscriberHandle &operator=(SubscriberHandle &&other) = default;
35
36 /**
37 * Manually unregister the associated event handler
38 */
39 void unregister() noexcept;
40
41 /**
42 * DTor. Automatically unregisters the associated event handler if not already disposed.
43 */
45
46 /**
47 * Ctor. Creates an empty handle
48 */
49 constexpr SubscriberHandle() noexcept = default;
50
51 /**
52 * Whether the handle refers to a valid event
53 * @return true if handle refers to an active event, false otherwise
54 */
55 [[nodiscard]] bool isSubscribed() const noexcept;
56
57 protected:
58
59 static constexpr Token Subscribe{};
60
61 /**
62 * Ctor
63 * creates a handle with specified id
64 * @param id id value of the handle
65 */
66 SubscriberHandle(Token);
67
68 private:
69 static constexpr char Alive = 1;
70 std::shared_ptr<char> alive;
71 };
72
73
74 namespace detail {
75 template<typename T>
76 class ActiveList {
77 std::vector<T> data{};
78 std::size_t endIdx{0};
79 public:
80 ActiveList() = default;
81
82 ActiveList(std::initializer_list<T> elems) : data(elems), endIdx(data.size()) {}
83
84 template<typename ...Args>
85 explicit ActiveList(Args &&... args) : data(std::forward<Args>(args)...), endIdx(data.size()) {}
86
87 ActiveList(const ActiveList &) = default;
88 ActiveList(ActiveList &&other) noexcept : ActiveList() {
89 swap(other);
90 }
91 ActiveList &operator=(ActiveList other) noexcept {
92 swap(other);
93 return *this;
94 }
95
96 ~ActiveList() = default;
97
98 void swap(ActiveList &other) noexcept {
99 using std::swap;
100 swap(data, other.data);
101 swap(endIdx, other.endIdx);
102 }
103
104 void markInactive(std::vector<T>::iterator elem) noexcept(std::is_nothrow_swappable_v<T>) {
105 if (empty()) {
106 return;
107 }
108
109 const auto pos = elem - data.begin();
110 if (pos < 0 or static_cast<std::size_t>(pos) >= endIdx) {
111 return;
112 }
113
114 --endIdx;
115 if (endIdx != static_cast<std::size_t>(pos)) {
116 std::swap(data[pos], data[endIdx]);
117 }
118 }
119
120 auto begin() const noexcept {
121 return data.begin();
122 }
123
124 auto begin() noexcept {
125 return data.begin();
126 }
127
128 auto end() const noexcept {
129 return data.begin() + endIdx;
130 }
131
132 auto end() noexcept {
133 return data.begin() + endIdx;
134 }
135
136 void cleanUp() {
137 data.erase(end(), data.end());
138 }
139
140 [[nodiscard]] std::size_t size() const noexcept {
141 return endIdx;
142 }
143
144 [[nodiscard]] bool empty() const noexcept {
145 return size() == 0;
146 }
147
148 template<typename ...Args>
149 void add(Args &&...args) {
150 if (endIdx == data.size()) {
151 data.emplace_back(std::forward<Args>(args)...);
152 } else {
153 data.pop_back();
154 data.emplace_back(std::forward<Args>(args)...);
155 std::swap(data.back(), data[endIdx]);
156 }
157
158 ++endIdx;
159 }
160
161 auto &back() const noexcept {
162 return data[endIdx - 1];
163 }
164 };
165
166 template<typename T>
167 void swap(ActiveList<T> &a, ActiveList<T> &b) noexcept {
168 a.swap(b);
169 }
170 }
171
172 /**
173 * @brief C# inspired event class that manages a list of event handlers that can be invoked together
174 * @tparam Args Arguments types of the event handler functions
175 */
176 template<typename ...Args>
178 using handler = std::function<void(Args...)>;
179 public:
180 constexpr SubscribableEvent() noexcept = default;
181
182 SubscribableEvent(const SubscribableEvent &) = delete;
183 SubscribableEvent &operator=(const SubscribableEvent &) = delete;
184 SubscribableEvent(SubscribableEvent &&) noexcept = default;
185 SubscribableEvent &operator=(SubscribableEvent &&) noexcept = default;
186
187 ~SubscribableEvent() {
188 for (auto &[_, handle]: handlers) {
189 handle.unregister();
190 }
191 }
192
193 /**
194 * Adds a functor to the event handler list. The functor is called when trigger is invoked
195 * @tparam Handler type of handler functor
196 * @param handlerFunction functor to be subscribed to the event
197 */
198 template<typename Handler>
199 void subscribe_unhandled(Handler &&handlerFunction) {
200 subscribe(std::forward<Handler>(handlerFunction), true);
201 }
202
203 /**
204 * @copydoc subscribe_unhandled
205 * @return handle to the event that is used to unsubscribe the handler (see SubscriberHandle)
206 * @note if the returned value is discarded the handler will be unregistered immediately after the call
207 */
208 template<typename Handler>
209 [[nodiscard]]SubscriberHandle subscribe_handled(Handler &&handlerFunction) {
210 return subscribe(std::forward<Handler>(handlerFunction), false);
211 }
212
213 /**
214 * Triggers the event. All subscribed event handlers are invoked with the provided arguments
215 * @param args arguments to the event handlers
216 */
217 template<typename ...InvokeArgs>
218 void trigger(InvokeArgs&&... args) const {
219 auto it = handlers.begin();
220 while (not handlers.empty() and it < handlers.end()) {
221 if (it->second.isSubscribed()) {
222 it->first(std::forward<InvokeArgs>(args)...);
223 ++it;
224 } else {
225 handlers.markInactive(it);
226 }
227 }
228 }
229
230 private:
231 template<typename Handler>
232 SubscriberHandle subscribe(Handler &&handlerFunction, bool discardHandler) {
233 static_assert(std::is_invocable_r_v<void, Handler, Args...>, "invalid event handler signature");
234 handlers.add(std::forward<Handler>(handlerFunction), SubscriberHandle(SubscriberHandle::Subscribe));
235 return discardHandler ? SubscriberHandle() : handlers.back().second;
236 }
237
238 mutable detail::ActiveList<std::pair<handler, SubscriberHandle>> handlers{};
239 };
240}
241
242#endif //SUBSCRIBABLEEVENT_HPP
C# inspired event class that manages a list of event handlers that can be invoked together.
Definition SubscribableEvent.hpp:177
void subscribe_unhandled(Handler &&handlerFunction)
Definition SubscribableEvent.hpp:199
void trigger(InvokeArgs &&... args) const
Definition SubscribableEvent.hpp:218
SubscriberHandle subscribe_handled(Handler &&handlerFunction)
Definition SubscribableEvent.hpp:209
Identifies a function handler subscribed to a SubscribableEvent. Can be used to unsubscribe the funct...
Definition SubscribableEvent.hpp:26
void unregister() noexcept
Definition SubscribableEvent.cpp:9
SubscriberHandle(Token)
Definition SubscribableEvent.cpp:21
constexpr SubscriberHandle() noexcept=default
bool isSubscribed() const noexcept
Definition SubscribableEvent.cpp:23
~SubscriberHandle()
Definition SubscribableEvent.cpp:17
Definition heuristics.hpp:33
Definition basic_structures.cpp:10