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/** Utilities for TSharedPtr/Ref and related */
20{
21 using namespace Mcro::Concepts;
22 using namespace Mcro::FunctionTraits;
23
24 /** Copy thread-safety from other shared object type */
25 template <CSharedOrWeak T>
26 using TSharedPtrFrom = TSharedPtr<typename T::ElementType, T::Mode>;
27
28 /** Copy thread-safety from other shared object type */
29 template <CSharedOrWeak T>
30 using TSharedRefFrom = TSharedRef<typename T::ElementType, T::Mode>;
31
32 /** Copy thread-safety from other shared object type */
33 template <CSharedOrWeak T>
34 using TWeakPtrFrom = TWeakPtr<typename T::ElementType, T::Mode>;
35
36 /**
37 * 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(Forward<Args>(args)...);
49 };
50
51 /**
52 * A wrapper around MakeShareable that automatically calls an initializer method Initialize on the
53 * instantiated object.
54 *
55 * This works around the annoyance of TSharedFromThis objects cannot use their shared pointers in their
56 * constructor, braking RAII in some cases. Of course this is only achievable if the object cooperates and
57 * implements an Initialize method.
58 * It's important that Initialize should not be virtual so derived classes can hide them with their own
59 * overload.
60 */
61 template <typename T, ESPMode Mode = ESPMode::ThreadSafe, typename... Args>
62 requires CSharedInitializeable<T, Args...>
63 TSharedRef<T, Mode> MakeShareableInit(T* newObject, Args&&... args)
64 {
65 TSharedRef<T, Mode> result = MakeShareable(newObject);
66 result->Initialize(Forward<Args>(args)...);
67 return result;
68 }
69
70 /**
71 * A combination of MakeShareable and `Mcro::Construct::ConstructNew`
72 * Usage:
73 *
74 * @code
75 * using namespace Mcro::SharedObjects;
76 *
77 * auto myObject = ConstructShared([](MyObject& _)
78 * {
79 * _.Foo = 42;
80 * _.Initialize();
81 * // etc...
82 * });
83 * static_assert(std::is_same_v<decltype(myObject), TSharedRef<MyObject>>);
84 * @endcode
85 *
86 * @param init A lambda function with a single l-value reference parameter of the object type to initialize.
87 * @param args
88 * @return A pointer to the object instance on heap.
89 * @remarks The C++ 20 designated initializers with named arguments has annoying limitations, therefore this exists
90 */
91 template <
92 CFunctorObject Initializer,
93 typename... Args,
94 ESPMode Mode = ESPMode::ThreadSafe,
95 typename ResultArg = TFunction_Arg<Initializer, 0>,
96 typename Result = std::decay_t<ResultArg>
97 >
98 requires std::is_lvalue_reference_v<ResultArg>
99 TSharedRef<Result, Mode> ConstructShared(Initializer&& init, Args&&... args)
100 {
101 using namespace Mcro::Construct;
102 return MakeShareable(
103 ConstructNew(Forward<Initializer>(init), Forward<Args>(args)...)
104 );
105 }
106
107 /**
108 * Create a shared pointer which takes in an object with in-place refcounting.
109 *
110 * Refcounted TSharedPtr/Ref doesn't take ownership of the object and when the last reference goes out of scope it
111 * simply decreases the refcount instead of deleting the object.
112 */
113 template <CRefCounted T, ESPMode Mode = ESPMode::ThreadSafe>
114 TSharedRef<T, Mode> ShareRefCounted(T* object)
115 {
116 check(object);
117 object->AddRef();
118 return MakeShareable(object, [](T* object) { object->Release(); });
119 }
120
121 /**
122 * Same as `SharedThis(this)` in `TSharedFromThis` but returning a weak pointer instead.
123 * Indeed `TSharedFromThis` already has `AsWeak()` but that can only return the type which was originally set by
124 * `TSharedFromThis` making its usage slightly less convenient in derived classes.
125 */
126 template <
127 CSharedFromThis T,
128 ESPMode Mode = decltype(DeclVal<T const>().AsShared())::Mode
129 >
130 auto WeakSelf(const T* self) -> TWeakPtr<T const, Mode>
131 {
132 return StaticCastSharedRef<T const>(self->AsShared());
133 }
134
135 /**
136 * Same as `SharedThis(this)` in `TSharedFromThis` but returning a weak pointer instead.
137 * Indeed `TSharedFromThis` already has `AsWeak()` but that can only return the type which was originally set by
138 * `TSharedFromThis` making its usage slightly less convenient in derived classes.
139 */
140 template <
141 CSharedFromThis T,
142 ESPMode Mode = decltype(DeclVal<T>().AsShared())::Mode
143 >
144 auto WeakSelf(T* self) -> TWeakPtr<T, Mode>
145 {
146 return StaticCastSharedRef<T>(self->AsShared());
147 }
148}
typename TFunctionTraits< std::decay_t< T > >::template Arg< I > TFunction_Arg
TSharedRef< T, Mode > MakeShareableInit(T *newObject, Args &&... args)
TSharedRef< typename T::ElementType, T::Mode > TSharedRefFrom
TSharedPtr< typename T::ElementType, T::Mode > TSharedPtrFrom
TWeakPtr< typename T::ElementType, T::Mode > TWeakPtrFrom
TSharedRef< T, Mode > ShareRefCounted(T *object)
auto WeakSelf(const T *self) -> TWeakPtr< T const, Mode >
TSharedRef< Result, Mode > ConstructShared(Initializer &&init, Args &&... args)