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 const T* operator -> (this Self&& self)
341 {
342 return &self.Get();
343 }
344 };
345
346 /**
347 * @brief
348 * Storage wrapper for any value which state needs to be tracked or their change needs to be observed.
349 * By default, TState is not thread-safe unless FStatePolicy::ThreadSafe policy is active in DefaultPolicy
350 *
351 * TState and IState allows developers an expressive API for tracking/syncing changes of a stateful entity. Like
352 * button presses, resolution changes or storing the last error. It doesn't only give a storage for the value but
353 * triggers events when the value changes. This alleviates the need for explicit `OnStuffChanged` events in the API
354 * where `TState` is used.
355 *
356 * Use `TState` on class members where a default set of policy can be declared in compile time, and use `IState`
357 * where these states should be referenced in a function argument for example. In the latter case the policy flags
358 * are erased so multiple states with different policies are compatible with each-other. Although that is not
359 * necessary and not recommended if we would only need to consume their value, as states can have implicit
360 * conversion to their value (returning a const-ref).
361 *
362 * If given value is equality comparable, TState will only trigger change events when the previous and the current
363 * values are different. Unless that behavior is overridden by `FStatePolicy` flags. A default set of flags are
364 * determined by `StatePolicyFor` template for any given type.
365 */
366 template <typename T, FStatePolicy DefaultPolicy>
367 struct TState : IState<T>
368 {
369 template <typename ThreadSafeType, typename NaiveType>
370 using ThreadSafeSwitch = std::conditional_t<DefaultPolicy.ThreadSafe, ThreadSafeType, NaiveType>;
371
373
374 using typename StateBase::ReadLockVariant;
375 using typename StateBase::WriteLockVariant;
376
379
380 static constexpr FStatePolicy DefaultPolicyFlags = DefaultPolicy;
381
382 /** @brief Enable default constructor only when T is default initializable */
383 template <CDefaultInitializable = T>
384 TState() : Value() {}
385
386 /** @brief Enable copy constructor for T only when T is copy constructable */
387 template <CCopyConstructible = T>
388 explicit TState(T const& value) : Value(value) {}
389
390 /** @brief Enable move constructor for T only when T is move constructable */
391 template <CMoveConstructible = T>
392 explicit TState(T&& value) : Value(MoveTemp(value)) {}
393
394 /** @brief Enable copy constructor for the state only when T is copy constructable */
395 template <CCopyConstructible = T>
396 TState(TState const& other) : Value(other.Value.Next) {}
397
398 /** @brief Enable move constructor for the state only when T is move constructable */
399 template <CMoveConstructible = T>
400 TState(TState&& other) : Value(MoveTemp(other.Value.Next)) {}
401
402 /** @brief Construct value in-place with non-semantic single argument constructor */
403 template <typename Arg>
404 requires (!CConvertibleTo<Arg, TState> && !CSameAs<Arg, T>)
405 TState(Arg&& arg) : Value(FWD(arg)) {}
406
407 /** @brief Construct value in-place with multiple argument constructor */
408 template <typename... Args>
409 requires (sizeof...(Args) > 1)
410 TState(Args&&... args) : Value(FWD(args)...) {}
411
412 virtual T const& Get() const override { return Value.Next; }
413
414 virtual TTuple<T const&, TUniquePtr<ReadLockVariant>> GetOnAnyThread() const override
415 {
416 return { Value.Next, ReadLock() };
417 }
418
419 virtual void Set(T const& value) override
420 {
421 ASSERT_QUIT(!Modifying, ,
422 ->WithMessage(TEXT_"Attempting to set this state while this state is already being set from somewhere else.")
423 );
424 TGuardValue modifyingGuard(Modifying, true);
425 auto lock = WriteLock();
426 bool allow = true;
427
428 if constexpr (CCoreEqualityComparable<T>)
429 allow = PolicyFlags.AlwaysNotify || Value.Next != value;
430
431 if constexpr (CCopyable<T>)
433 Value.Previous = Value.Next;
434
435 if (allow)
436 {
437 Value.Next = value;
438 OnChangeEvent.Broadcast(Value);
439 }
440 }
441
442 virtual void Modify(TUniqueFunction<void(T&)>&& modifier, bool alwaysNotify = true) override
443 {
444 ASSERT_QUIT(!Modifying, ,
445 ->WithMessage(TEXT_"Attempting to set this state while this state is already being set from somewhere else.")
446 );
447 TGuardValue modifyingGuard(Modifying, true);
448 auto lock = WriteLock();
449 bool allow = true;
450 TOptional<T> previous;
451
452 if constexpr (CCopyable<T>)
454 previous = Value.Next;
455
456 modifier(Value.Next);
457
458 if constexpr (CCopyable<T> && CCoreEqualityComparable<T>)
459 allow = alwaysNotify
461 || !Value.Previous.IsSet()
462 || previous.GetValue() != Value.Next;
463
464 if constexpr (CCopyable<T>)
466 Value.Previous = previous;
467
468 if (allow)
469 OnChangeEvent.Broadcast(Value);
470 }
471
472 protected:
473 virtual FDelegateHandle OnChangeImpl(TDelegate<void(TChangeData<T> const&)>&& onChange, FEventPolicy const& eventPolicy = {}) override
474 {
475 auto lock = WriteLock();
476 return OnChangeEvent.Add(onChange, eventPolicy);
477 }
478
479 public:
480 virtual bool Remove(FDelegateHandle const& handle) override
481 {
482 auto lock = WriteLock();
483 return OnChangeEvent.Remove(handle);
484 }
485
486 virtual int32 RemoveAll(const void* object) override
487 {
488 auto lock = WriteLock();
489 return OnChangeEvent.RemoveAll(object);
490 }
491
492 virtual bool HasChangedFrom(const T& nextValue) override
493 {
494 if constexpr (CCoreEqualityComparable<T>)
495 {
496 bool hasChanged = Value.Next != nextValue;
497 Set(nextValue);
498 return hasChanged;
499 }
500 else
501 {
502 Set(nextValue);
503 return true;
504 }
505 }
506
507 virtual bool HasEverChanged() const override
508 {
509 return OnChangeEvent.IsBroadcasted();
510 }
511
512 virtual TUniquePtr<ReadLockVariant> ReadLock() const override
513 {
514 return MakeUnique<ReadLockVariant>(TInPlaceType<ReadLockType>(), Mutex.Get());
515 }
516
517 virtual TUniquePtr<WriteLockVariant> WriteLock() override
518 {
519 return MakeUnique<WriteLockVariant>(TInPlaceType<WriteLockType>(), Mutex.Get());
520 }
521
522 virtual TOptional<T> const& GetPrevious() const override
523 {
524 return Value.Previous;
525 }
526
527 virtual T const& GetPrevious(T const& fallback) const override
528 {
529 return Value.Previous.Get(fallback);
530 }
531
532 virtual T const& GetPreviousOrCurrent() const override
533 {
534 return Value.Previous.IsSet() ? Value.Previous.GetValue() : Value.Next;
535 }
536
537 virtual void NormalizePrevious() override
538 {
539 if (!PolicyFlags.StorePrevious) return;
540 ASSERT_QUIT(!Modifying, ,
541 ->WithMessage(TEXT_"Attempting to set this state while this state is already being set from somewhere else.")
542 );
543 TGuardValue modifyingGuard(Modifying, true);
544 auto lock = WriteLock();
545 Value.Previous = Value.Next;
546 }
547
548 template <CConvertibleTo<T> Other>
549 requires (!CState<Other>)
550 TState& operator = (Other&& value)
551 {
552 if constexpr (CCopyable<Other>)
553 Set(value);
554 else if constexpr (CMovable<Other>)
555 Set(MoveTemp(value));
556 return *this;
557 }
558
559 FStatePolicy PolicyFlags { DefaultPolicy };
560
561 private:
562 TEventDelegate<void(TChangeData<T> const&)> OnChangeEvent;
563 TChangeData<T> Value;
564 bool Modifying = false;
565 mutable TInitializeOnCopy<FRWLock> Mutex;
566 };
567
568 template <typename LeftValue, CWeaklyEqualityComparableWith<LeftValue> RightValue>
569 bool operator == (IState<LeftValue> const& left, IState<RightValue> const& right)
570 {
571 return left.Get() == right.Get();
572 }
573
574 template <typename LeftValue, CPartiallyOrderedWith<LeftValue> RightValue>
575 bool operator <=> (IState<LeftValue> const& left, IState<RightValue> const& right)
576 {
577 return left.Get() <=> right.Get();
578 }
579}
#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:569
bool operator<=>(IState< LeftValue > const &left, IState< RightValue > const &right)
Definition Observable.h:575
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....
const T * operator->(this Self &&self)
Definition Observable.h:340
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
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:368
virtual TUniquePtr< WriteLockVariant > WriteLock() override
Lock this state for writing for the current scope.
Definition Observable.h:517
TState(T &&value)
Enable move constructor for T only when T is move constructable.
Definition Observable.h:392
virtual int32 RemoveAll(const void *object) override
Equivalent to TMulticastDelegate::RemoveAll
Definition Observable.h:486
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:419
std::conditional_t< DefaultPolicy.ThreadSafe, ThreadSafeType, NaiveType > ThreadSafeSwitch
Definition Observable.h:370
virtual FDelegateHandle OnChangeImpl(TDelegate< void(TChangeData< T > const &)> &&onChange, FEventPolicy const &eventPolicy={}) override
Definition Observable.h:473
virtual bool Remove(FDelegateHandle const &handle) override
Equivalent to TMulticastDelegate::Remove
Definition Observable.h:480
TState(TState const &other)
Enable copy constructor for the state only when T is copy constructable.
Definition Observable.h:396
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:442
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:532
static constexpr FStatePolicy DefaultPolicyFlags
Definition Observable.h:380
virtual TUniquePtr< ReadLockVariant > ReadLock() const override
Lock this state for reading for the current scope.
Definition Observable.h:512
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:527
TState(Arg &&arg)
Construct value in-place with non-semantic single argument constructor.
Definition Observable.h:405
TState()
Enable default constructor only when T is default initializable.
Definition Observable.h:384
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:414
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:492
ThreadSafeSwitch< FReadScopeLock, FVoid > ReadLockType
Definition Observable.h:377
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:522
ThreadSafeSwitch< FWriteScopeLock, FVoid > WriteLockType
Definition Observable.h:378
virtual void NormalizePrevious() override
Set the previous value to the current one. Useful in Ticks.
Definition Observable.h:537
TState(Args &&... args)
Construct value in-place with multiple argument constructor.
Definition Observable.h:410
TState(TState &&other)
Enable move constructor for the state only when T is move constructable.
Definition Observable.h:400
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:412
TState(T const &value)
Enable copy constructor for T only when T is copy constructable.
Definition Observable.h:388
virtual bool HasEverChanged() const override
Returns true if this state has ever been changed from its initial value given at construction.
Definition Observable.h:507