Clio  develop
The XRP Ledger API server.
Loading...
Searching...
No Matches
ObservableValue.hpp
1#pragma once
2
3#include <boost/signals2/connection.hpp>
4#include <boost/signals2/signal.hpp>
5#include <boost/signals2/variadic_signal.hpp>
6
7#include <atomic>
8#include <concepts>
9#include <type_traits>
10
11namespace util {
12
13template <typename T>
14concept SomeAtomic =
15 std::same_as<std::remove_cvref_t<T>, std::atomic<std::remove_cvref_t<typename T::value_type>>>;
16
30template <typename T>
31concept Observable =
32 std::equality_comparable<T> && std::copy_constructible<T> && std::move_constructible<T>;
33
34namespace impl {
35
45template <Observable T>
47protected:
48 boost::signals2::signal<void(T const&)> onUpdate_;
49
50public:
51 virtual ~ObservableValueBase() = default;
52
58 boost::signals2::connection
59 observe(std::invocable<T const&> auto&& fn)
60 {
61 return onUpdate_.connect(std::forward<decltype(fn)>(fn));
62 }
63
68 [[nodiscard]] bool
70 {
71 return not onUpdate_.empty();
72 }
73
80 virtual void
82
83protected:
88 void
89 notifyObservers(T const& value)
90 {
91 onUpdate_(value);
92 }
93};
94
95} // namespace impl
96
97// Forward declaration
98template <typename T>
100
123template <Observable T>
124 requires(not SomeAtomic<T>)
125class ObservableValue<T> : public impl::ObservableValueBase<T> {
126 T value_;
127
138 struct ObservableGuard {
139 T const oldValue;
140 ObservableValue<T>& ref;
141
146 ObservableGuard(ObservableValue<T>& observable) : oldValue(observable), ref(observable)
147 {
148 }
149
156 ~ObservableGuard()
157 {
158 if (oldValue != ref.value_)
159 ref.notifyObservers(ref.value_);
160 }
161
166 [[nodiscard]]
167 operator T&()
168 {
169 return ref.value_;
170 }
171 };
172
173public:
178 ObservableValue(std::convertible_to<T> auto&& value)
179 : value_{std::forward<decltype(value)>(value)}
180 {
181 }
182
187 requires std::default_initializable<T>
188 : value_{}
189 {
190 }
191
192 ObservableValue(ObservableValue const&) = delete;
193 ObservableValue(ObservableValue&&) = default;
194 ObservableValue&
195 operator=(ObservableValue const&) = delete;
196 ObservableValue&
197 operator=(ObservableValue&&) = default;
198
210 ObservableValue&
211 operator=(std::convertible_to<T> auto&& val)
212 {
213 set(val);
214 return *this;
215 }
216
225 [[nodiscard]] ObservableGuard
227 {
228 return {*this};
229 }
230
235 [[nodiscard]]
236 operator T const&() const
237 {
238 return value_;
239 }
240
245 [[nodiscard]] T const&
246 get() const
247 {
248 return value_;
249 }
250
266 void
267 set(std::convertible_to<T> auto&& val)
268 {
269 if (value_ != val) {
270 value_ = std::forward<decltype(val)>(val);
271 this->notifyObservers(value_);
272 }
273 }
274
281 void
282 forceNotify() override
283 {
284 this->notifyObservers(value_);
285 }
286};
287
307template <Observable T>
308class ObservableValue<std::atomic<T>> : public impl::ObservableValueBase<T> {
309 std::atomic<T> value_;
310
311public:
316 ObservableValue(std::convertible_to<T> auto&& value)
317 : value_{std::forward<decltype(value)>(value)}
318 {
319 }
320
325 requires std::default_initializable<T>
326 : value_{}
327 {
328 }
329
330 ObservableValue(ObservableValue const&) = delete;
331 ObservableValue(ObservableValue&&) = default;
332 ObservableValue&
333 operator=(ObservableValue const&) = delete;
334 ObservableValue&
335 operator=(ObservableValue&&) = default;
336
346 ObservableValue&
347 operator=(std::convertible_to<T> auto&& val)
348 {
349 set(std::forward<decltype(val)>(val));
350 return *this;
351 }
352
357 [[nodiscard]] T
358 get() const
359 {
360 return value_.load();
361 }
362
367 [[nodiscard]]
368 operator T() const
369 {
370 return get();
371 }
372
381 void
382 set(std::convertible_to<T> auto&& val)
383 {
384 T newValue = std::forward<decltype(val)>(val);
385 T oldValue = value_.load();
386
387 // Use compare-and-swap to atomically update
388 while (!value_.compare_exchange_weak(oldValue, newValue)) {
389 // compare_exchange_weak updates oldValue with current value on failure
390 // Continue until we succeed
391 }
392
393 // Notify observers if we actually changed the value
394 // Note: oldValue now contains the actual previous value that was replaced
395 if (oldValue != newValue) {
396 this->notifyObservers(newValue);
397 }
398 }
399
406 void
407 forceNotify() override
408 {
409 this->notifyObservers(value_.load());
410 }
411};
412
413} // namespace util
ObservableValue & operator=(std::convertible_to< T > auto &&val)
Assignment operator that updates value and notifies observers.
Definition ObservableValue.hpp:211
ObservableGuard operator->()
Provides deferred notification access to the value.
Definition ObservableValue.hpp:226
void forceNotify() override
Forces notification of all observers with the current value.
Definition ObservableValue.hpp:282
ObservableValue()
Constructs ObservableValue with default initial value.
Definition ObservableValue.hpp:186
ObservableValue(std::convertible_to< T > auto &&value)
Constructs ObservableValue with initial value.
Definition ObservableValue.hpp:178
T const & get() const
Explicitly gets the current value.
Definition ObservableValue.hpp:246
void set(std::convertible_to< T > auto &&val)
Sets a new value and notifies observers if changed.
Definition ObservableValue.hpp:267
void set(std::convertible_to< T > auto &&val)
Sets a new atomic value and notifies observers if changed.
Definition ObservableValue.hpp:382
ObservableValue()
Constructs ObservableValue with default initial value.
Definition ObservableValue.hpp:324
T get() const
Gets the current atomic value.
Definition ObservableValue.hpp:358
ObservableValue(std::convertible_to< T > auto &&value)
Constructs ObservableValue with initial atomic value.
Definition ObservableValue.hpp:316
ObservableValue & operator=(std::convertible_to< T > auto &&val)
Assignment operator that updates atomic value and notifies observers.
Definition ObservableValue.hpp:347
void forceNotify() override
Forces notification of all observers with the current value.
Definition ObservableValue.hpp:407
Definition ObservableValue.hpp:99
Base class containing common ObservableValue functionality.
Definition ObservableValue.hpp:46
void notifyObservers(T const &value)
Notifies all observers with the given value.
Definition ObservableValue.hpp:89
boost::signals2::connection observe(std::invocable< T const & > auto &&fn)
Registers an observer callback for value changes.
Definition ObservableValue.hpp:59
bool hasObservers() const
Checks if there are any active observers.
Definition ObservableValue.hpp:69
virtual void forceNotify()=0
Forces notification of all observers with the current value.
Concept defining types that can be observed for changes.
Definition ObservableValue.hpp:31
Definition ObservableValue.hpp:14
This namespace contains various utilities.
Definition AccountUtils.hpp:11