Ark Server API (ASA) - Wiki
Loading...
Searching...
No Matches
PimplPtr.h
Go to the documentation of this file.
1// Copyright Epic Games, Inc. All Rights Reserved.
2
3#pragma once
4
5#include "CoreTypes.h"
6#include "Templates/UnrealTemplate.h"
7
8// Single-ownership smart pointer similar to TUniquePtr but with a few differences which make it
9// particularly useful for (but not limited to) implementing the pimpl pattern:
10//
11// https://en.cppreference.com/w/cpp/language/pimpl
12//
13// Some of the features:
14//
15// Like TUniquePtr:
16// - Unique ownership - no reference counting.
17// - Move-only, no copying by default.
18// - Has the same static footprint as a pointer.
19//
20// Like TSharedPtr:
21// - The deleter is determined at binding time and type-erased, allowing the object to be deleted without access to the definition of the type.
22// - Has additional heap footprint (but smaller than TSharedPtr).
23//
24// Unlike both:
25// - No custom deleter support.
26// - No derived->base pointer conversion support (impossible to implement in C++ in a good way with multiple inheritance, and not typically needed for pimpls).
27// - The pointed-to object must be created with its Make function - it cannot take ownership of an existing pointer.
28// - No array support.
29//
30// The main benefits of this class which make it useful for pimpls:
31// - Has single-ownership semantics.
32// - Has the same performance and footprint as a pointer to the object, and minimal overhead on construction and destruction.
33// - Can be added as a class member with a forward-declared type without having to worry about the proper definition of constructors and other special member functions.
34// - Can support deep copying, including with forward declared types
35
36
37/**
38 * Specifies the copy mode for TPimplPtr
39 */
40enum class EPimplPtrMode : uint8
41{
42 /** Don't support copying (default) */
43 NoCopy,
44
45 /** Support deep copying, including of forward declared types */
47};
48
49// Forward declaration
50template<typename T, EPimplPtrMode Mode = EPimplPtrMode::NoCopy> struct TPimplPtr;
51
52
53namespace UE::Core::Private::PimplPtr
54{
55 inline constexpr SIZE_T RequiredAlignment = 16;
56
57 template <typename T>
58 struct TPimplHeapObjectImpl;
59
60
61 template <typename T>
62 void DeleterFunc(void* Ptr)
63 {
64 // We never pass a null pointer to this function, but the compiler emits delete code
65 // which handles nulls - we don't need this extra branching code, so assume it's not.
67 delete (TPimplHeapObjectImpl<T>*)Ptr;
68 }
69
70 template<typename T>
71 static void* CopyFunc(void* A)
72 {
74
76
77 return &NewHeap->Val;
78 };
79
80 using FDeleteFunc = void(*)(void*);
81 using FCopyFunc = void*(*)(void*);
82
83 template <typename T>
84 struct TPimplHeapObjectImpl
85 {
86 enum class ENoCopyType { ConstructType };
88
89 template <typename... ArgTypes>
90 explicit TPimplHeapObjectImpl(ENoCopyType, ArgTypes&&... Args)
91 : Val(Forward<ArgTypes>(Args)...)
92 {
93 // This should never fire, unless a compiler has laid out this struct in an unexpected way
94 static_assert(STRUCT_OFFSET(TPimplHeapObjectImpl, Val) == RequiredAlignment,
95 "Unexpected alignment of T within the pimpl object");
96 }
97
98 template <typename... ArgTypes>
99 explicit TPimplHeapObjectImpl(EDeepCopyType, ArgTypes&&... Args)
100 : Copier(&CopyFunc<T>)
101 , Val(Forward<ArgTypes>(Args)...)
102 {
103 // This should never fire, unless a compiler has laid out this struct in an unexpected way
104 static_assert(STRUCT_OFFSET(TPimplHeapObjectImpl, Val) == RequiredAlignment,
105 "Unexpected alignment of T within the pimpl object");
106 }
107
108 explicit TPimplHeapObjectImpl(void* InVal)
109 : Copier(&CopyFunc<T>)
110 , Val(*(T*)InVal)
111 {
112 }
113
114
115 FDeleteFunc Deleter = &DeleterFunc<T>;
116 FCopyFunc Copier = nullptr;
117
118 alignas(RequiredAlignment) T Val;
119 };
120
121 FORCEINLINE void CallDeleter(void* Ptr)
122 {
123 void* ThunkedPtr = (char*)Ptr - RequiredAlignment;
124
125 // 'noexcept' as part of a function signature is a C++17 feature, but its use here
126 // can tidy up the codegen a bit. As we're likely to build with exceptions disabled
127 // anyway, this is not something we need a well-engineered solution for right now,
128 // so it's simply left commented out until we can rely on it everywhere.
129 (*(void(**)(void*) /*noexcept*/)ThunkedPtr)(ThunkedPtr);
130 }
131
132 FORCEINLINE void* CallCopier(void* Ptr)
133 {
134 void* BasePtr = (char*)Ptr - RequiredAlignment;
135 void* ThunkedPtr = (char*)BasePtr + sizeof(FDeleteFunc);
136
137 return (*(FCopyFunc*)ThunkedPtr)(Ptr);
138 }
139}
140
141
142template <typename T>
144{
145private:
146 template <typename, EPimplPtrMode> friend struct TPimplPtr;
147
148 template <typename U, EPimplPtrMode M, typename... ArgTypes>
149 friend TPimplPtr<U, M> MakePimpl(ArgTypes&&... Args);
150
151 explicit TPimplPtr(UE::Core::Private::PimplPtr::TPimplHeapObjectImpl<T>* Impl)
152 : Ptr(&Impl->Val)
153 {
154 }
155
156public:
157 TPimplPtr() = default;
158
159 TPimplPtr(TYPE_OF_NULLPTR)
160 {
161 }
162
164 {
165 if (Ptr)
166 {
168 }
169 }
170
171 TPimplPtr(const TPimplPtr&) = delete;
172 TPimplPtr& operator=(const TPimplPtr&) = delete;
173
174 // Movable
176 : Ptr(Other.Ptr)
177 {
178 Other.Ptr = nullptr;
179 }
180
182 {
183 if (&Other != this)
184 {
185 T* LocalPtr = this->Ptr;
186 this->Ptr = Other.Ptr;
187 Other.Ptr = nullptr;
188 if (LocalPtr)
189 {
191 }
192 }
193 return *this;
194 }
195
196 TPimplPtr& operator=(TYPE_OF_NULLPTR)
197 {
198 Reset();
199 return *this;
200 }
201
202 bool IsValid() const
203 {
204 return !!this->Ptr;
205 }
206
207 explicit operator bool() const
208 {
209 return !!this->Ptr;
210 }
211
212 T* operator->() const
213 {
214 return this->Ptr;
215 }
216
217 T* Get() const
218 {
219 return this->Ptr;
220 }
221
222 T& operator*() const
223 {
224 return *this->Ptr;
225 }
226
227 void Reset()
228 {
229 if (T* LocalPtr = this->Ptr)
230 {
231 this->Ptr = nullptr;
233 }
234 }
235
236 FORCEINLINE bool operator==(TYPE_OF_NULLPTR) { return !IsValid(); }
237 FORCEINLINE bool operator!=(TYPE_OF_NULLPTR) { return IsValid(); }
238
239private:
240 T* Ptr = nullptr;
241};
242
243template <typename T>
245{
246private:
248
249 template <typename U, EPimplPtrMode M, typename... ArgTypes>
250 friend TPimplPtr<U, M> MakePimpl(ArgTypes&&... Args);
251
252 // Super here breaks clang
254
255public:
256 TPimplPtr() = default;
257 ~TPimplPtr() = default;
258
260 {
261 if (A.IsValid())
262 {
263 this->Ptr = (T*)UE::Core::Private::PimplPtr::CallCopier(A.Ptr);
264 }
265 }
266
268 {
269 if (&A != this)
270 {
271 if (IsValid())
272 {
273 Reset();
274 }
275
276 if (A.IsValid())
277 {
278 this->Ptr = (T*)UE::Core::Private::PimplPtr::CallCopier(A.Ptr);
279 }
280 }
281
282 return *this;
283 }
284
285 TPimplPtr(TPimplPtr&&) = default;
287
288 TPimplPtr(TYPE_OF_NULLPTR)
289 {
290 }
291
292 FORCEINLINE TPimplPtr& operator=(TYPE_OF_NULLPTR A)
293 {
294 Super::operator = (A);
295 return *this;
296 }
297
298 using Super::IsValid;
299 using Super::operator bool;
300 using Super::operator ->;
301 using Super::Get;
302 using Super::operator *;
303 using Super::Reset;
304};
305
307template <typename T, EPimplPtrMode Mode> FORCEINLINE bool operator==(TYPE_OF_NULLPTR, const TPimplPtr<T, Mode>& Ptr) { return !Ptr.IsValid(); }
308template <typename T, EPimplPtrMode Mode> FORCEINLINE bool operator!=(TYPE_OF_NULLPTR, const TPimplPtr<T, Mode>& Ptr) { return Ptr.IsValid(); }
309#endif
310
311/**
312 * Heap-allocates an instance of T with the given arguments and returns it as a TPimplPtr.
313 *
314 * Usage: TPimplPtr<FMyType> MyPtr = MakePimpl<FMyType>(...arguments...);
315 *
316 * DeepCopy Usage: TPimplPtr<FMyType, EPimplPtrMode::DeepCopy> MyPtr = MakePimpl<FMyType, EPimplPtrMode::DeepCopy>(...arguments...);
317 */
318template <typename T, EPimplPtrMode Mode = EPimplPtrMode::NoCopy, typename... ArgTypes>
319FORCEINLINE TPimplPtr<T, Mode> MakePimpl(ArgTypes&&... Args)
320{
321 using FHeapType = UE::Core::Private::PimplPtr::TPimplHeapObjectImpl<T>;
322 using FHeapConstructType = typename std::conditional<Mode == EPimplPtrMode::NoCopy, typename FHeapType::ENoCopyType,
323 typename FHeapType::EDeepCopyType>::type;
324
325 static_assert(Mode != EPimplPtrMode::DeepCopy ||
326 std::is_copy_constructible<T>::value, "T must be a copyable type, to use with EPimplPtrMode::DeepCopy");
327 static_assert(sizeof(T) > 0, "T must be a complete type");
328 static_assert(alignof(T) <= UE::Core::Private::PimplPtr::RequiredAlignment, "T cannot be aligned more than 16 bytes");
329
330 return TPimplPtr<T, Mode>(new FHeapType(FHeapConstructType::ConstructType, Forward<ArgTypes>(Args)...));
331}
EPimplPtrMode
Definition Enums.h:12183
FORCEINLINE bool operator!=(TYPE_OF_NULLPTR, const TPimplPtr< T, Mode > &Ptr)
Definition PimplPtr.h:308
FORCEINLINE TPimplPtr< T, Mode > MakePimpl(ArgTypes &&... Args)
Definition PimplPtr.h:319
FORCEINLINE bool operator==(TYPE_OF_NULLPTR, const TPimplPtr< T, Mode > &Ptr)
Definition PimplPtr.h:307
#define FORCEINLINE
Definition Platform.h:644
#define UE_ASSUME(x)
Definition Platform.h:715
#define PLATFORM_COMPILER_HAS_GENERATED_COMPARISON_OPERATORS
Definition Platform.h:250
#define STRUCT_OFFSET(struc, member)
constexpr SIZE_T RequiredAlignment
Definition PimplPtr.h:55
FORCEINLINE void * CallCopier(void *Ptr)
Definition PimplPtr.h:132
void DeleterFunc(void *Ptr)
Definition PimplPtr.h:62
static void * CopyFunc(void *A)
Definition PimplPtr.h:71
FORCEINLINE void CallDeleter(void *Ptr)
Definition PimplPtr.h:121
Definition Vector.h:40
FORCEINLINE TPimplPtr & operator=(TYPE_OF_NULLPTR A)
Definition PimplPtr.h:292
TPimplPtr & operator=(TPimplPtr &&)=default
TPimplPtr & operator=(const TPimplPtr &A)
Definition PimplPtr.h:267
friend TPimplPtr< U, M > MakePimpl(ArgTypes &&... Args)
Definition PimplPtr.h:319
FORCEINLINE bool operator==(TYPE_OF_NULLPTR)
Definition PimplPtr.h:236
TPimplPtr(const TPimplPtr &)=delete
TPimplPtr(UE::Core::Private::PimplPtr::TPimplHeapObjectImpl< T > *Impl)
Definition PimplPtr.h:151
TPimplPtr & operator=(TYPE_OF_NULLPTR)
Definition PimplPtr.h:196
TPimplPtr & operator=(TPimplPtr &&Other)
Definition PimplPtr.h:181
TPimplPtr & operator=(const TPimplPtr &)=delete
FORCEINLINE bool operator!=(TYPE_OF_NULLPTR)
Definition PimplPtr.h:237
TPimplHeapObjectImpl(EDeepCopyType, ArgTypes &&... Args)
Definition PimplPtr.h:99
TPimplHeapObjectImpl(ENoCopyType, ArgTypes &&... Args)
Definition PimplPtr.h:90