MCRO
C++23 utilities for Unreal Engine.
Loading...
Searching...
No Matches
SharedObjects.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
14#include "CoreMinimal.h"
15#include "Mcro/FunctionTraits.h"
16#include "Mcro/Construct.h"
17
18/** @brief Utilities for TSharedPtr/Ref and related */
20{
21 using namespace Mcro::Concepts;
22 using namespace Mcro::FunctionTraits;
23
24 /** @brief Copy thread-safety from other shared object type */
25 template <CSharedOrWeak T>
26 using TSharedPtrFrom = TSharedPtr<typename T::ElementType, T::Mode>;
27
28 /** @brief Copy thread-safety from other shared object type */
29 template <CSharedOrWeak T>
30 using TSharedRefFrom = TSharedRef<typename T::ElementType, T::Mode>;
31
32 /** @brief Copy thread-safety from other shared object type */
33 template <CSharedOrWeak T>
34 using TWeakPtrFrom = TWeakPtr<typename T::ElementType, T::Mode>;
35
36 /**
37 * @brief Concept describing an object which provides a deferred initializer for shared objects.
38 *
39 * This works around the annoyance of TSharedFromThis objects cannot use their shared pointers in their
40 * constructor, braking RAII in some cases. Of course this is only achievable if the object cooperates and
41 * implements an Initialize method.
42 * It's important that `Initialize` should not be virtual so derived classes can hide them with their own
43 * overload.
44 */
45 template <typename T, typename... Args>
46 concept CSharedInitializeable = requires(T& object, Args&&... args)
47 {
48 object.Initialize(FWD(args)...);
49 };
50
51 /**
52 * @brief
53 * A wrapper around MakeShareable that automatically calls an initializer method Initialize on the
54 * instantiated object.
55 *
56 * This works around the annoyance of TSharedFromThis objects cannot use their shared pointers in their
57 * constructor, braking RAII in some cases. Of course this is only achievable if the object cooperates and
58 * implements an Initialize method.
59 * It's important that Initialize should not be virtual so derived classes can hide them with their own
60 * overload.
61 */
62 template <typename T, ESPMode Mode = ESPMode::ThreadSafe, typename... Args>
63 requires CSharedInitializeable<T, Args...>
64 TSharedRef<T, Mode> MakeShareableInit(T* newObject, Args&&... args)
65 {
66 TSharedRef<T, Mode> result = MakeShareable(newObject);
67 result->Initialize(FWD(args)...);
68 return result;
69 }
70
71 /**
72 * @brief A combination of MakeShareable and Mcro::Construct::ConstructNew
73 *
74 * Usage:
75 * @code
76 * using namespace Mcro::SharedObjects;
77 *
78 * auto myObject = ConstructShared([](MyObject& _)
79 * {
80 * _.Foo = 42;
81 * _.Initialize();
82 * // etc...
83 * });
84 * static_assert(std::is_same_v<decltype(myObject), TSharedRef<MyObject>>);
85 * @endcode
86 *
87 * @param init A lambda function with a single l-value reference parameter of the object type to initialize.
88 * @param args Arguments of the object constructor
89 * @return A pointer to the object instance on heap.
90 * @remarks The C++ 20 designated initializers with named arguments has annoying limitations, therefore this exists
91 */
92 template <
93 CFunctorObject Initializer,
94 typename... Args,
95 ESPMode Mode = ESPMode::ThreadSafe,
96 typename ResultArg = TFunction_Arg<Initializer, 0>,
97 typename Result = std::decay_t<ResultArg>
98 >
99 requires std::is_lvalue_reference_v<ResultArg>
100 TSharedRef<Result, Mode> ConstructShared(Initializer&& init, Args&&... args)
101 {
102 using namespace Mcro::Construct;
103 return MakeShareable(
104 ConstructNew(FWD(init), FWD(args)...)
105 );
106 }
107
108 /**
109 * @brief Create a shared pointer which takes in an object with in-place refcounting.
110 *
111 * Refcounted TSharedPtr/Ref doesn't take ownership of the object and when the last reference goes out of scope it
112 * simply decreases the refcount instead of deleting the object.
113 */
114 template <CRefCounted T, ESPMode Mode = ESPMode::ThreadSafe>
115 TSharedRef<T, Mode> ShareRefCounted(T* object)
116 {
117 check(object);
118 object->AddRef();
119 return MakeShareable(object, [](T* object) { object->Release(); });
120 }
121
122 /**
123 * @brief
124 * Same as `SharedThis(this)` in `TSharedFromThis` but returning a weak pointer instead.
125 *
126 * Indeed, `TSharedFromThis` already has `AsWeak()` but that can only return the type which was originally set by
127 * `TSharedFromThis` making its usage slightly less convenient in derived classes.
128 */
129 template <
130 CSharedFromThis T,
131 ESPMode Mode = decltype(DeclVal<T const>().AsShared())::Mode
132 >
133 auto WeakSelf(const T* self) -> TWeakPtr<T const, Mode>
134 {
135 return StaticCastSharedRef<std::decay_t<T> const>(self->AsShared());
136 }
137
138 /**
139 * @brief
140 * Same as `SharedThis(this)` in `TSharedFromThis` but returning a weak pointer instead.
141 *
142 * Indeed, `TSharedFromThis` already has `AsWeak()` but that can only return the type which was originally set by
143 * `TSharedFromThis` making its usage slightly less convenient in derived classes.
144 */
145 template <
146 CSharedFromThis T,
147 ESPMode Mode = decltype(DeclVal<T>().AsShared())::Mode
148 >
149 auto WeakSelf(T* self) -> TWeakPtr<T, Mode>
150 {
151 return StaticCastSharedRef<std::decay_t<T>>(self->AsShared());
152 }
153
154 /** @brief Same as `SharedThis(this)` in `TSharedFromThis`. */
155 template <
156 CSharedFromThis T,
157 ESPMode Mode = decltype(DeclVal<T const>().AsShared())::Mode
158 >
159 auto SharedSelf(const T* self) -> TSharedRef<T const, Mode>
160 {
161 return StaticCastSharedRef<std::decay_t<T> const>(self->AsShared());
162 }
163
164 /** @brief Same as `SharedThis(this)` in `TSharedFromThis`. */
165 template <
166 CSharedFromThis T,
167 ESPMode Mode = decltype(DeclVal<T>().AsShared())::Mode
168 >
169 auto SharedSelf(T* self) -> TSharedRef<T, Mode>
170 {
171 return StaticCastSharedRef<std::decay_t<T>>(self->AsShared());
172 }
173
174 /**
175 * @brief
176 * A simple mutable shared storage of any value.
177 *
178 * One particular use case for this may be delegates which may remove themselves once invoked. For example:
179 * @code
180 * auto once = MakeSharedDelegateHandle();
181 * once->Value = MyEvent.AddLambda([this, once]
182 * {
183 * // execute something only once.
184 * MyEvent.Remove(once->Value);
185 * });
186 * @endcode
187 */
188 template <CDefaultInitializable T>
190 {
192 };
193
194 template <CDefaultInitializable T>
195 using TSharedStoragePtr = TSharedPtr<TSharedStorage<T>>;
196
197 template <CDefaultInitializable T>
198 using TSharedStorageRef = TSharedRef<TSharedStorage<T>>;
199
200 template <CDefaultInitializable T>
201 using TSharedStorageWeakPtr = TWeakPtr<TSharedStorage<T>>;
202
203 /** @brief A simple convenience wrapper around making FDelegateHandles shared */
205 {
206 return MakeShared<TSharedStorage<FDelegateHandle>>();
207 }
208}
#define FWD(...)
Shorten forwarding expression with this macro so one may not need to specify explicit type.
Definition Macros.h:100
Concept describing an object which provides a deferred initializer for shared objects.
typename TFunctionTraits< std::decay_t< T > >::template Arg< I > TFunction_Arg
Shorthand for getting a type of a function argument at given position I.
Utilities for TSharedPtr/Ref and related.
TSharedRef< T, Mode > MakeShareableInit(T *newObject, Args &&... args)
A wrapper around MakeShareable that automatically calls an initializer method Initialize on the insta...
auto SharedSelf(const T *self) -> TSharedRef< T const, Mode >
Same as SharedThis(this) in TSharedFromThis.
TSharedRef< typename T::ElementType, T::Mode > TSharedRefFrom
Copy thread-safety from other shared object type.
TSharedPtr< typename T::ElementType, T::Mode > TSharedPtrFrom
Copy thread-safety from other shared object type.
TSharedRef< TSharedStorage< T > > TSharedStorageRef
TWeakPtr< typename T::ElementType, T::Mode > TWeakPtrFrom
Copy thread-safety from other shared object type.
FORCEINLINE TSharedStorageRef< FDelegateHandle > MakeSharedDelegateHandle()
A simple convenience wrapper around making FDelegateHandles shared.
TSharedRef< T, Mode > ShareRefCounted(T *object)
Create a shared pointer which takes in an object with in-place refcounting.
auto WeakSelf(const T *self) -> TWeakPtr< T const, Mode >
Same as SharedThis(this) in TSharedFromThis but returning a weak pointer instead.
TWeakPtr< TSharedStorage< T > > TSharedStorageWeakPtr
TSharedRef< Result, Mode > ConstructShared(Initializer &&init, Args &&... args)
A combination of MakeShareable and Mcro::Construct::ConstructNew.
TSharedPtr< TSharedStorage< T > > TSharedStoragePtr
A simple mutable shared storage of any value.