MCRO
C++23 utilities for Unreal Engine.
Loading...
Searching...
No Matches
Observable.h
Go to the documentation of this file.
1/** @noop License Comment
2 * @file
3 * @copyright
4 * This Source Code is subject to the terms of the Mozilla Public License, v2.0.
5 * If a copy of the MPL was not distributed with this file You can obtain one at
6 * https://mozilla.org/MPL/2.0/
7 *
8 * @author David Mórász
9 * @date 2025
10 */
11
12#pragma once
13#include "CoreMinimal.h"
14#include "Mcro/AssertMacros.h"
16#include "Mcro/Observable.Fwd.h"
17
18namespace Mcro::Observable
19{
20 using namespace Mcro::Delegates;
21 using namespace Mcro::InitializeOnCopy;
22
23 /**
24 * @brief
25 * This struct holds the circumstances of the data change. It cannot be moved or copied and its lifespan is
26 * managed entirely by `TState`
27 */
28 template <typename T>
30 {
31 template <CDefaultInitializable = T>
33
34 template <CMoveConstructible = T>
35 TChangeData(T&& value) : Next(FWD(value)) {}
36
37 template <CCopyConstructible = T>
38 TChangeData(const TChangeData& from) : Next(from.Next), Previous(from.Previous) {}
39
40 template <CMoveConstructible = T>
41 TChangeData(TChangeData&& from) noexcept : Next(MoveTemp(from.Next)), Previous(MoveTemp(from.Previous)) {}
42
43 template <typename Arg>
44 requires (!CSameAs<Arg, TChangeData> && !CSameAs<Arg, T>)
45 TChangeData(Arg&& arg) : Next(FWD(arg)) {}
46
47 template <typename... Args>
48 requires (sizeof...(Args) > 1)
49 TChangeData(Args&&... args) : Next(FWD(args)...) {}
50
52 TOptional<T> Previous;
53 };
54
55 /** @brief Public API and base class for `TState` which shouldn't concern with policy flags or thread safety */
56 template <typename T>
58 {
59 using Type = T;
60 using ReadLockVariant = TVariant<FReadScopeLock, FVoid>;
61 using WriteLockVariant = TVariant<FWriteScopeLock, FVoid>;
62
63 virtual ~IState() = default;
64
65 /**
66 * @brief
67 * Get the wrapped value if for some reason the conversion operator is not enough or deleted.
68 * Thread safety is not considered in this function, use `ReadLock` before `Get`, or use `GetOnAnyThread`
69 * which provides a read lock, if thread safety is a concern.
70 */
71 virtual T const& Get() const = 0;
72
73 /**
74 * @brief
75 * Set the wrapped value if for some reason the assignment operator is not enough or deleted. When thread
76 * safety is enabled Set will automatically lock this state for writing.
77 *
78 * @warning
79 * Setting this state from within its change listeners is prohibited and will trigger a check()
80 */
81 virtual void Set(T const& value) = 0;
82
83 /**
84 * @brief Modify this state via an l-value ref in a functor
85 *
86 * @param modifier The functor which modifies this value
87 *
88 * @param alwaysNotify
89 * Notify observers about the change even when the previous state is not different after the modification.
90 * This is only applicable when T is copyable, comparable, StorePrevious flag is set and AlwaysNotify flag is
91 * not set via policy.
92 */
93 virtual void Modify(TUniqueFunction<void(T&)>&& modifier, bool alwaysNotify = true) = 0;
94
95 protected:
96 template <CChangeListener<T> Function>
97 static auto DelegateValueArgument(Function const& onChange)
98 {
99 return [onChange](TChangeData<T> const& change)
100 {
102 onChange(change.Next);
104 onChange(change.Next, change.Previous);
105 };
106 }
107
108 virtual FDelegateHandle OnChangeImpl(TDelegate<void(TChangeData<T> const&)>&& onChange, FEventPolicy const& eventPolicy = {}) = 0;
109 public:
110
111 /** @brief Add a delegate which gets a `TChangeData<T> const&` if this state has been set. */
112 FDelegateHandle OnChange(TDelegate<void(TChangeData<T> const&)> onChange, FEventPolicy const& eventPolicy = {})
113 {
114 return OnChangeImpl(MoveTemp(onChange), eventPolicy);
115 };
116
117 /**
118 * @brief
119 * Add a function without object binding which either has one or two arguments with the following signature:
120 * `[](T const& next, [TOptional<T> const& previous])`
121 *
122 * Where the argument `previous` is optional (to have, not its type). The argument `previous` when it is
123 * present is TOptional because it may only have a value when StorePrevious policy is active and T is copyable.
124 */
125 template <CChangeListener<T> Function>
126 FDelegateHandle OnChange(Function const& onChange, FEventPolicy const& eventPolicy = {})
127 {
128 return OnChange(InferDelegate::From(DelegateValueArgument(onChange)), eventPolicy);
129 }
130
131 /**
132 * @brief
133 * Add a function with an object binding which either has one or two arguments with the following signature:
134 * `[](T const& next, [TOptional<T> const& previous])`
135 *
136 * Where the argument `previous` is optional (to have, not its type). The argument `previous` when it is
137 * present is TOptional because it may only have a value when StorePrevious policy is active and T is copyable.
138 */
139 template <typename Object, CChangeListener<T> Function>
140 FDelegateHandle OnChange(Object&& object, Function const& onChange, FEventPolicy const& eventPolicy = {})
141 {
142 return OnChange(InferDelegate::From(FWD(object), DelegateValueArgument(onChange)), eventPolicy);
143 }
144
145 /**
146 * @brief Pull changes from another state, syncing the value between the two. Values will be copied.
147 *
148 * @tparam Other Type convertible to the value of this state
149 * @tparam Guard The type of the lifespan guarding object
150 *
151 * @param object
152 * A lifespan guarding object in the context of this state, shared/weak pointer or UObject recommended.
153 *
154 * @param otherState A reference to the other state.
155 */
156 template <CConvertibleToDecayed<T> Other, typename Guard>
157 void SyncPull(Guard&& object, IState<Other>& otherState)
158 {
159 otherState.OnChange(
160 FWD(object),
161 [this](Other const& next) { Set(next); },
162 {.Belated = true}
163 );
164 }
165
166 /**
167 * @brief Push changes from another state, syncing the value between the two. Values will be copied.
168 *
169 * @tparam Other The value of this state convertible to this type
170 * @tparam Guard The type of the lifespan guarding object
171 *
172 * @param object
173 * A lifespan guarding object in the context of the other state, shared/weak pointer or UObject recommended.
174 *
175 * @param otherState A reference to the other state.
176 */
177 template <typename Other, typename Guard>
178 requires CConvertibleToDecayed<Other, T>
179 void SyncPush(Guard&& object, IState<Other>& otherState)
180 {
181 OnChange(
182 FWD(object),
183 [&otherState](T const& next) { otherState.Set(next); },
184 {.Belated = true}
185 );
186 }
187
188 /**
189 * @brief
190 * Given value will be stored in the state only if T is equality comparable and it differs from the current
191 * state value. If T is not equality comparable this function is equivalent to Set and always returns true.
192 *
193 * @return
194 * True if the given value was different from the previous state value. Always returns true when T is is not
195 * equality comparable.
196 */
197 virtual bool HasChangedFrom(const T& nextValue) = 0;
198
199 /** @brief Returns true if this state has ever been changed from its initial value given at construction. */
200 virtual bool HasEverChanged() const = 0;
201
202 /** @brief Equivalent to `TMulticastDelegate::Remove` */
203 virtual bool Remove(FDelegateHandle const& handle) = 0;
204
205 /** @brief Equivalent to `TMulticastDelegate::RemoveAll` */
206 virtual int32 RemoveAll(const void* object) = 0;
207
208 /**
209 * @brief
210 * If thread safety is enabled in DefaultPolicy, get the value with a bundled read-scope-lock. Otherwise the
211 * tuple returns an empty dummy struct as its second argument.
212 *
213 * Use C++17 structured binding for convenience:
214 * @code
215 * auto [value, lock] = MyState.GetOnAnyThread();
216 * @endcode
217 *
218 * @remarks
219 * Unlike the placeholder `auto` keyword, the structured binding `auto` keyword preserves reference qualifiers.
220 * See https://godbolt.org/z/jn918fKfd
221 *
222 * @return
223 * The lock is returned as TUniquePtr it's slightly more expensive because of ref-counting but it makes the API
224 * so much easier to use as TState can decide to return a real lock or just a dummy.
225 */
226 virtual TTuple<T const&, TUniquePtr<ReadLockVariant>> GetOnAnyThread() const = 0;
227
228 /**
229 * @brief Lock this state for reading for the current scope.
230 *
231 * @return
232 * The lock is returned as TUniquePtr it's slightly more expensive because of ref-counting but it makes the API
233 * so much easier to use as TState can decide to return a real lock or just a dummy.
234 */
235 virtual TUniquePtr<ReadLockVariant> ReadLock() const = 0;
236
237 /**
238 * @brief Lock this state for writing for the current scope.
239 *
240 * @return
241 * The lock is returned as TUniquePtr it's slightly more expensive because of ref-counting but it makes the API
242 * so much easier to use as TState can decide to return a real lock or just a dummy.
243 */
244 virtual TUniquePtr<WriteLockVariant> WriteLock() = 0;
245
246 /** @brief Get the previous value if StorePrevious is enabled and there was at least one change */
247 virtual TOptional<T> const& GetPrevious() const = 0;
248
249 /**
250 * @brief
251 * Get the previous value if StorePrevious is enabled and there was at least one change. Get a fallback value
252 * otherwise.
253 *
254 * @param fallback Return this value if there's no previous one available
255 */
256 virtual T const& GetPrevious(T const& fallback) const = 0;
257
258 /**
259 * @brief
260 * Get the previous value if StorePrevious is enabled and there was at least one change or the current value
261 * otherwise.
262 */
263 virtual T const& GetPreviousOrCurrent() const = 0;
264
265 /**
266 * @brief Set the previous value to the current one. Useful in Ticks.
267 *
268 * It will not trigger change notifications but it will use a write-lock when thread-safety is enabled.
269 * If `StorePrevious` is disabled it will do nothing.
270 */
271 virtual void NormalizePrevious() = 0;
272
273 /**
274 * @brief Returns true when this state is currently true, but previously it wasn't
275 *
276 * @param fallback Use this as the fallback previous state.
277 */
278 template <CBooleanTestable = T>
279 bool BecameTrue(bool fallback = false) const
280 {
281 bool previous = GetPrevious().IsSet()
282 ? static_cast<bool>(GetPrevious().GetValue())
283 : fallback;
284
285 return static_cast<bool>(Get()) && !previous;
286 }
287
288 /**
289 * @brief Returns true when this state is currently true, but previously it wasn't
290 *
291 * @param fallback Use this as the fallback previous state.
292 */
293 template <CBooleanTestable = T>
294 bool OnDown(bool fallback = false) const { return BecameTrue(fallback); }
295
296 /**
297 * @brief Returns true when this state is currently false, but previously it wasn't
298 *
299 * @param fallback Use this as the fallback previous state.
300 */
301 template <CBooleanTestable = T>
302 bool BecameFalse(bool fallback = false) const
303 {
304 bool previous = GetPrevious().IsSet()
305 ? static_cast<bool>(GetPrevious().GetValue())
306 : fallback;
307
308 return !static_cast<bool>(Get()) && previous;
309 }
310
311 /**
312 * @brief Returns true when this state is currently false, but previously it wasn't
313 *
314 * @param fallback Use this as the fallback previous state.
315 */
316 template <CBooleanTestable = T>
317 bool OnUp(bool fallback = false) const { return BecameFalse(); }
318
319 /** @brief Returns true when current value is not equal to previous one. */
320 template <CCoreEqualityComparable = T>
321 bool HasChanged() const
322 {
323 return Get() != GetPreviousOrCurrent();
324 }
325
326 /** @brief Returns true when current value is not equal to previous one. */
327 template <CCoreEqualityComparable = T>
328 bool HasChanged(T const& fallback) const
329 {
330 return Get() != GetPrevious(fallback);
331 }
332
333 template <typename Self>
334 operator T const& (this Self&& self)
335 {
336 return self.Get();
337 }
338
339 template <typename Self>
340 auto&& operator -> (this Self&& self)
341 {
342 if constexpr (CPointer<T>)
343 return self.Get();
344
345 else if constexpr (CMemberAccessible<T>)
346 return self.Get().operator->();
347
348 else return &self.Get();
349 }
350 };
351
352 /**
353 * @brief
354 * Storage wrapper for any value which state needs to be tracked or their change needs to be observed.
355 * By default, TState is not thread-safe unless FStatePolicy::ThreadSafe policy is active in DefaultPolicy
356 *
357 * TState and IState allows developers an expressive API for tracking/syncing changes of a stateful entity. Like
358 * button presses, resolution changes or storing the last error. It doesn't only give a storage for the value but
359 * triggers events when the value changes. This alleviates the need for explicit `OnStuffChanged` events in the API
360 * where `TState` is used.
361 *
362 * Use `TState` on class members where a default set of policy can be declared in compile time, and use `IState`
363 * where these states should be referenced in a function argument for example. In the latter case the policy flags
364 * are erased so multiple states with different policies are compatible with each-other. Although that is not
365 * necessary and not recommended if we would only need to consume their value, as states can have implicit
366 * conversion to their value (returning a const-ref).
367 *
368 * If given value is equality comparable, TState will only trigger change events when the previous and the current
369 * values are different. Unless that behavior is overridden by `FStatePolicy` flags. A default set of flags are
370 * determined by `StatePolicyFor` template for any given type.
371 */
372 template <typename T, FStatePolicy DefaultPolicy>
373 struct TState : IState<T>
374 {
375 template <typename ThreadSafeType, typename NaiveType>
376 using ThreadSafeSwitch = std::conditional_t<DefaultPolicy.ThreadSafe, ThreadSafeType, NaiveType>;
377
379
380 using typename StateBase::ReadLockVariant;
381 using typename StateBase::WriteLockVariant;
382
385
386 static constexpr FStatePolicy DefaultPolicyFlags = DefaultPolicy;
387
388 /** @brief Enable default constructor only when T is default initializable */
389 template <CDefaultInitializable = T>
390 TState() : Value() {}
391
392 /** @brief Enable copy constructor for T only when T is copy constructable */
393 template <CCopyConstructible = T>
394 explicit TState(T const& value) : Value(value) {}
395
396 /** @brief Enable move constructor for T only when T is move constructable */
397 template <CMoveConstructible = T>
398 explicit TState(T&& value) : Value(MoveTemp(value)) {}
399
400 /** @brief Enable copy constructor for the state only when T is copy constructable */
401 template <CCopyConstructible = T>
402 TState(TState const& other) : Value(other.Value.Next) {}
403
404 /** @brief Enable move constructor for the state only when T is move constructable */
405 template <CMoveConstructible = T>
406 TState(TState&& other) : Value(MoveTemp(other.Value.Next)) {}
407
408 /** @brief Construct value in-place with non-semantic single argument constructor */
409 template <typename Arg>
410 requires (!CConvertibleTo<Arg, TState> && !CSameAs<Arg, T>)
411 TState(Arg&& arg) : Value(FWD(arg)) {}
412
413 /** @brief Construct value in-place with multiple argument constructor */
414 template <typename... Args>
415 requires (sizeof...(Args) > 1)
416 TState(Args&&... args) : Value(FWD(args)...) {}
417
418 virtual T const& Get() const override { return Value.Next; }
419
420 virtual TTuple<T const&, TUniquePtr<ReadLockVariant>> GetOnAnyThread() const override
421 {
422 return { Value.Next, ReadLock() };
423 }
424
425 virtual void Set(T const& value) override
426 {
427 ASSERT_QUIT(!Modifying, ,
428 ->WithMessage(TEXT_"Attempting to set this state while this state is already being set from somewhere else.")
429 );
430 TGuardValue modifyingGuard(Modifying, true);
431 auto lock = WriteLock();
432 bool allow = true;
433
434 if constexpr (CCoreEqualityComparable<T>)
435 allow = PolicyFlags.AlwaysNotify || Value.Next != value;
436
437 if constexpr (CCopyable<T>)
439 Value.Previous = Value.Next;
440
441 if (allow)
442 {
443 Value.Next = value;
444 OnChangeEvent.Broadcast(Value);
445 }
446 }
447
448 virtual void Modify(TUniqueFunction<void(T&)>&& modifier, bool alwaysNotify = true) override
449 {
450 ASSERT_QUIT(!Modifying, ,
451 ->WithMessage(TEXT_"Attempting to set this state while this state is already being set from somewhere else.")
452 );
453 TGuardValue modifyingGuard(Modifying, true);
454 auto lock = WriteLock();
455 bool allow = true;
456 TOptional<T> previous;
457
458 if constexpr (CCopyable<T>)
460 previous = Value.Next;
461
462 modifier(Value.Next);
463
464 if constexpr (CCopyable<T> && CCoreEqualityComparable<T>)
465 allow = alwaysNotify
467 || !Value.Previous.IsSet()
468 || previous.GetValue() != Value.Next;
469
470 if constexpr (CCopyable<T>)
472 Value.Previous = previous;
473
474 if (allow)
475 OnChangeEvent.Broadcast(Value);
476 }
477
478 protected:
479 virtual FDelegateHandle OnChangeImpl(TDelegate<void(TChangeData<T> const&)>&& onChange, FEventPolicy const& eventPolicy = {}) override
480 {
481 auto lock = WriteLock();
482 return OnChangeEvent.Add(onChange, eventPolicy);
483 }
484
485 public:
486 virtual bool Remove(FDelegateHandle const& handle) override
487 {
488 auto lock = WriteLock();
489 return OnChangeEvent.Remove(handle);
490 }
491
492 virtual int32 RemoveAll(const void* object) override
493 {
494 auto lock = WriteLock();
495 return OnChangeEvent.RemoveAll(object);
496 }
497
498 virtual bool HasChangedFrom(const T& nextValue) override
499 {
500 if constexpr (CCoreEqualityComparable<T>)
501 {
502 bool hasChanged = Value.Next != nextValue;
503 Set(nextValue);
504 return hasChanged;
505 }
506 else
507 {
508 Set(nextValue);
509 return true;
510 }
511 }
512
513 virtual bool HasEverChanged() const override
514 {
515 return OnChangeEvent.IsBroadcasted();
516 }
517
518 virtual TUniquePtr<ReadLockVariant> ReadLock() const override
519 {
520 return MakeUnique<ReadLockVariant>(TInPlaceType<ReadLockType>(), Mutex.Get());
521 }
522
523 virtual TUniquePtr<WriteLockVariant> WriteLock() override
524 {
525 return MakeUnique<WriteLockVariant>(TInPlaceType<WriteLockType>(), Mutex.Get());
526 }
527
528 virtual TOptional<T> const& GetPrevious() const override
529 {
530 return Value.Previous;
531 }
532
533 virtual T const& GetPrevious(T const& fallback) const override
534 {
535 return Value.Previous.Get(fallback);
536 }
537
538 virtual T const& GetPreviousOrCurrent() const override
539 {
540 return Value.Previous.IsSet() ? Value.Previous.GetValue() : Value.Next;
541 }
542
543 virtual void NormalizePrevious() override
544 {
545 if (!PolicyFlags.StorePrevious) return;
546 ASSERT_QUIT(!Modifying, ,
547 ->WithMessage(TEXT_"Attempting to set this state while this state is already being set from somewhere else.")
548 );
549 TGuardValue modifyingGuard(Modifying, true);
550 auto lock = WriteLock();
551 Value.Previous = Value.Next;
552 }
553
554 template <CConvertibleTo<T> Other>
555 requires (!CState<Other>)
556 TState& operator = (Other&& value)
557 {
558 if constexpr (CCopyable<Other>)
559 Set(value);
560 else if constexpr (CMovable<Other>)
561 Set(MoveTemp(value));
562 return *this;
563 }
564
565 FStatePolicy PolicyFlags { DefaultPolicy };
566
567 private:
568 TEventDelegate<void(TChangeData<T> const&)> OnChangeEvent;
569 TChangeData<T> Value;
570 bool Modifying = false;
571 mutable TInitializeOnCopy<FRWLock> Mutex;
572 };
573
574 template <typename LeftValue, CWeaklyEqualityComparableWith<LeftValue> RightValue>
575 bool operator == (IState<LeftValue> const& left, IState<RightValue> const& right)
576 {
577 return left.Get() == right.Get();
578 }
579
580 template <typename LeftValue, CPartiallyOrderedWith<LeftValue> RightValue>
581 bool operator <=> (IState<LeftValue> const& left, IState<RightValue> const& right)
582 {
583 return left.Get() <=> right.Get();
584 }
585}
#define ASSERT_QUIT(condition, returnOnFailure,...)
Use this instead of check macro if the checked expression shouldn't be ignored in shipping builds....
#define FWD(...)
Shorten forwarding expression with this macro so one may not need to specify explicit type.
Definition Macros.h:100
#define TEXT_
A convenience alternative to Unreal's own TEXT macro but this one doesn't require parenthesis around ...
Definition TextMacros.h:53
"Extension" of a common TMulticastDelegate. It allows to define optional "flags" when adding a bindin...
Concept describing a function which can listen to changes to the current value of a TState only.
Concept describing a function which can listen to changes to the current and the previous values of a...
Concept constraining given type to a state.
TInferredDelegate< Function, Captures... > From(Function func, Captures &&... captures)
Instead of specifying manually a delegate type, infer it from the input function and the extra captur...
bool operator==(IState< LeftValue > const &left, IState< RightValue > const &right)
Definition Observable.h:575
bool operator<=>(IState< LeftValue > const &left, IState< RightValue > const &right)
Definition Observable.h:581
Settings for the TEventDelegate class, which defines optional behavior when adding a binding to it.
A type wrapper around a default initializeable object which may not be copyable but which needs to be...
Flags expressing how TState should handle object comparison and lifespan.
bool StorePrevious
Store previous value as well. If the value is equality comparable store only when it's changed.
bool AlwaysStorePrevious
If the state value is equality comparable, store the previous value even when that's equal to the new...
bool AlwaysNotify
Always emit change notification when a value is set on TState and don't attempt to compare them.
Public API and base class for TState which shouldn't concern with policy flags or thread safety.
Definition Observable.h:58
virtual T const & GetPreviousOrCurrent() const =0
Get the previous value if StorePrevious is enabled and there was at least one change or the current v...
bool HasChanged(T const &fallback) const
Returns true when current value is not equal to previous one.
Definition Observable.h:328
virtual void NormalizePrevious()=0
Set the previous value to the current one. Useful in Ticks.
virtual int32 RemoveAll(const void *object)=0
Equivalent to TMulticastDelegate::RemoveAll
FDelegateHandle OnChange(TDelegate< void(TChangeData< T > const &)> onChange, FEventPolicy const &eventPolicy={})
Add a delegate which gets a TChangeData<T> const& if this state has been set.
Definition Observable.h:112
virtual void Set(T const &value)=0
Set the wrapped value if for some reason the assignment operator is not enough or deleted....
virtual bool HasEverChanged() const =0
Returns true if this state has ever been changed from its initial value given at construction.
bool OnDown(bool fallback=false) const
Returns true when this state is currently true, but previously it wasn't.
Definition Observable.h:294
bool BecameTrue(bool fallback=false) const
Returns true when this state is currently true, but previously it wasn't.
Definition Observable.h:279
virtual void Modify(TUniqueFunction< void(T &)> &&modifier, bool alwaysNotify=true)=0
Modify this state via an l-value ref in a functor.
TVariant< FWriteScopeLock, FVoid > WriteLockVariant
Definition Observable.h:61
virtual TOptional< T > const & GetPrevious() const =0
Get the previous value if StorePrevious is enabled and there was at least one change.
bool BecameFalse(bool fallback=false) const
Returns true when this state is currently false, but previously it wasn't.
Definition Observable.h:302
virtual bool Remove(FDelegateHandle const &handle)=0
Equivalent to TMulticastDelegate::Remove
virtual T const & GetPrevious(T const &fallback) const =0
Get the previous value if StorePrevious is enabled and there was at least one change....
virtual bool HasChangedFrom(const T &nextValue)=0
Given value will be stored in the state only if T is equality comparable and it differs from the curr...
virtual T const & Get() const =0
Get the wrapped value if for some reason the conversion operator is not enough or deleted....
bool OnUp(bool fallback=false) const
Returns true when this state is currently false, but previously it wasn't.
Definition Observable.h:317
virtual FDelegateHandle OnChangeImpl(TDelegate< void(TChangeData< T > const &)> &&onChange, FEventPolicy const &eventPolicy={})=0
void SyncPull(Guard &&object, IState< Other > &otherState)
Pull changes from another state, syncing the value between the two. Values will be copied.
Definition Observable.h:157
FDelegateHandle OnChange(Function const &onChange, FEventPolicy const &eventPolicy={})
Add a function without object binding which either has one or two arguments with the following signat...
Definition Observable.h:126
FDelegateHandle OnChange(Object &&object, Function const &onChange, FEventPolicy const &eventPolicy={})
Add a function with an object binding which either has one or two arguments with the following signat...
Definition Observable.h:140
void SyncPush(Guard &&object, IState< Other > &otherState)
Push changes from another state, syncing the value between the two. Values will be copied.
Definition Observable.h:179
virtual TUniquePtr< ReadLockVariant > ReadLock() const =0
Lock this state for reading for the current scope.
bool HasChanged() const
Returns true when current value is not equal to previous one.
Definition Observable.h:321
TVariant< FReadScopeLock, FVoid > ReadLockVariant
Definition Observable.h:60
virtual TTuple< T const &, TUniquePtr< ReadLockVariant > > GetOnAnyThread() const =0
If thread safety is enabled in DefaultPolicy, get the value with a bundled read-scope-lock....
static auto DelegateValueArgument(Function const &onChange)
Definition Observable.h:97
virtual TUniquePtr< WriteLockVariant > WriteLock()=0
Lock this state for writing for the current scope.
virtual ~IState()=default
auto && operator->(this Self &&self)
Definition Observable.h:340
This struct holds the circumstances of the data change. It cannot be moved or copied and its lifespan...
Definition Observable.h:30
TChangeData(Args &&... args)
Definition Observable.h:49
TChangeData(const TChangeData &from)
Definition Observable.h:38
TChangeData(TChangeData &&from) noexcept
Definition Observable.h:41
Storage wrapper for any value which state needs to be tracked or their change needs to be observed....
Definition Observable.h:374
virtual TUniquePtr< WriteLockVariant > WriteLock() override
Lock this state for writing for the current scope.
Definition Observable.h:523
TState(T &&value)
Enable move constructor for T only when T is move constructable.
Definition Observable.h:398
virtual int32 RemoveAll(const void *object) override
Equivalent to TMulticastDelegate::RemoveAll
Definition Observable.h:492
virtual void Set(T const &value) override
Set the wrapped value if for some reason the assignment operator is not enough or deleted....
Definition Observable.h:425
std::conditional_t< DefaultPolicy.ThreadSafe, ThreadSafeType, NaiveType > ThreadSafeSwitch
Definition Observable.h:376
virtual FDelegateHandle OnChangeImpl(TDelegate< void(TChangeData< T > const &)> &&onChange, FEventPolicy const &eventPolicy={}) override
Definition Observable.h:479
virtual bool Remove(FDelegateHandle const &handle) override
Equivalent to TMulticastDelegate::Remove
Definition Observable.h:486
TState(TState const &other)
Enable copy constructor for the state only when T is copy constructable.
Definition Observable.h:402
virtual void Modify(TUniqueFunction< void(T &)> &&modifier, bool alwaysNotify=true) override
Modify this state via an l-value ref in a functor.
Definition Observable.h:448
virtual T const & GetPreviousOrCurrent() const override
Get the previous value if StorePrevious is enabled and there was at least one change or the current v...
Definition Observable.h:538
static constexpr FStatePolicy DefaultPolicyFlags
Definition Observable.h:386
virtual TUniquePtr< ReadLockVariant > ReadLock() const override
Lock this state for reading for the current scope.
Definition Observable.h:518
virtual T const & GetPrevious(T const &fallback) const override
Get the previous value if StorePrevious is enabled and there was at least one change....
Definition Observable.h:533
TState(Arg &&arg)
Construct value in-place with non-semantic single argument constructor.
Definition Observable.h:411
TState()
Enable default constructor only when T is default initializable.
Definition Observable.h:390
virtual TTuple< T const &, TUniquePtr< ReadLockVariant > > GetOnAnyThread() const override
If thread safety is enabled in DefaultPolicy, get the value with a bundled read-scope-lock....
Definition Observable.h:420
virtual bool HasChangedFrom(const T &nextValue) override
Given value will be stored in the state only if T is equality comparable and it differs from the curr...
Definition Observable.h:498
ThreadSafeSwitch< FReadScopeLock, FVoid > ReadLockType
Definition Observable.h:383
virtual TOptional< T > const & GetPrevious() const override
Get the previous value if StorePrevious is enabled and there was at least one change.
Definition Observable.h:528
ThreadSafeSwitch< FWriteScopeLock, FVoid > WriteLockType
Definition Observable.h:384
virtual void NormalizePrevious() override
Set the previous value to the current one. Useful in Ticks.
Definition Observable.h:543
TState(Args &&... args)
Construct value in-place with multiple argument constructor.
Definition Observable.h:416
TState(TState &&other)
Enable move constructor for the state only when T is move constructable.
Definition Observable.h:406
virtual T const & Get() const override
Get the wrapped value if for some reason the conversion operator is not enough or deleted....
Definition Observable.h:418
TState(T const &value)
Enable copy constructor for T only when T is copy constructable.
Definition Observable.h:394
virtual bool HasEverChanged() const override
Returns true if this state has ever been changed from its initial value given at construction.
Definition Observable.h:513