Plasma Engine  2.0
Loading...
Searching...
No Matches
DelegateHelper_inl.h
1
3struct PL_FOUNDATION_DLL plLambdaDelegateStorageBase
4{
6 virtual ~plLambdaDelegateStorageBase() = default;
7 virtual plLambdaDelegateStorageBase* Clone(plAllocator* pAllocator) const = 0;
8 virtual void InplaceCopy(plUInt8* pBuffer) const = 0;
9 virtual void InplaceMove(plUInt8* pBuffer) = 0;
10
11private:
16};
17
18template <typename Function>
20{
21 plLambdaDelegateStorage(Function&& func)
22 : m_func(std::move(func))
23 {
24 }
25
26private:
27 template <typename = typename std::enable_if<std::is_copy_constructible<Function>::value>>
28 plLambdaDelegateStorage(const Function& func)
29 : m_func(func)
30 {
31 }
32
33public:
34 virtual plLambdaDelegateStorageBase* Clone(plAllocator* pAllocator) const override
35 {
36 if constexpr (std::is_copy_constructible<Function>::value)
37 {
38 return PL_NEW(pAllocator, plLambdaDelegateStorage<Function>, m_func);
39 }
40 else
41 {
42 PL_REPORT_FAILURE("The plDelegate stores a lambda that is not copyable. Copying this plDelegate is not supported.");
43 return nullptr;
44 }
45 }
46
47 virtual void InplaceCopy(plUInt8* pBuffer) const override
48 {
49 if constexpr (std::is_copy_constructible<Function>::value)
50 {
51 new (pBuffer) plLambdaDelegateStorage<Function>(m_func);
52 }
53 else
54 {
55 PL_REPORT_FAILURE("The plDelegate stores a lambda that is not copyable. Copying this plDelegate is not supported.");
56 }
57 }
58
59 virtual void InplaceMove(plUInt8* pBuffer) override
60 {
61 if constexpr (std::is_move_constructible<Function>::value)
62 {
63 new (pBuffer) plLambdaDelegateStorage<Function>(std::move(m_func));
64 }
65 else
66 {
67 PL_REPORT_FAILURE("The plDelegate stores a lambda that is not movable. Moving this plDelegate is not supported.");
68 }
69 }
70
71 Function m_func;
72};
73
74
75template <typename R, class... Args, plUInt32 DataSize>
76struct plDelegate<R(Args...), DataSize> : public plDelegateBase
77{
78private:
79 using SelfType = plDelegate<R(Args...), DataSize>;
80 constexpr const void* HeapLambda() const { return reinterpret_cast<const void*>((size_t)-1); }
81 constexpr const void* InplaceLambda() const { return reinterpret_cast<const void*>((size_t)-2); }
82
83public:
84 PL_ALWAYS_INLINE plDelegate()
85 : m_DispatchFunction(nullptr)
86 {
87 }
88
89 PL_ALWAYS_INLINE plDelegate(const SelfType& other) { *this = other; }
90
91 PL_ALWAYS_INLINE plDelegate(SelfType&& other) { *this = std::move(other); }
92
94 template <typename Method, typename Class>
95 PL_FORCE_INLINE plDelegate(Method method, Class* pInstance)
96 {
97 CopyMemberFunctionToInplaceStorage(method);
98
99 m_Instance.m_Ptr = pInstance;
100 m_DispatchFunction = &DispatchToMethod<Method, Class>;
101 }
102
104 template <typename Method, typename Class>
105 PL_FORCE_INLINE plDelegate(Method method, const Class* pInstance)
106 {
107 CopyMemberFunctionToInplaceStorage(method);
108
109 m_Instance.m_ConstPtr = pInstance;
110 m_DispatchFunction = &DispatchToConstMethod<Method, Class>;
111 }
112
114 template <typename Function>
115 PL_FORCE_INLINE plDelegate(Function function, plAllocator* pAllocator = plFoundation::GetDefaultAllocator())
116 {
117 static_assert(DataSize >= 16, "DataSize must be at least 16 bytes");
118
119 // Pure function pointers or lambdas that can be cast into pure functions (no captures) can be
120 // copied directly into the inplace storage of the delegate.
121 // Lambdas with captures need to be wrapped into an plLambdaDelegateStorage object as they can
122 // capture non-pod or non-memmoveable data. This wrapper can also be stored inplace if it is small enough,
123 // otherwise it will be heap allocated with the specified allocator.
124 constexpr size_t functionSize = sizeof(Function);
125 using signature = R(Args...);
126 if constexpr (functionSize <= DataSize && std::is_assignable<signature*&, Function>::value)
127 {
128 // Lambdas with no capture have a size of 1.
129 // Lambdas with no capture actually have no data. Do not copy the 1 uninitialized byte.
130 // Propper function pointers have a size of > 4 or 8 (depending on pointer size)
131 if constexpr (functionSize > 1)
132 {
133 CopyFunctionToInplaceStorage(function);
134 }
135 else
136 {
137 memset(m_Data, 0, DataSize);
138 }
139
140 m_Instance.m_ConstPtr = nullptr;
141 m_DispatchFunction = &DispatchToFunction<Function>;
142 }
143 else
144 {
145 constexpr size_t storageSize = sizeof(plLambdaDelegateStorage<Function>);
146 if constexpr (storageSize <= DataSize)
147 {
148 m_Instance.m_ConstPtr = InplaceLambda();
149 new (m_Data) plLambdaDelegateStorage<Function>(std::move(function));
150 memset(m_Data + storageSize, 0, DataSize - storageSize);
151 m_DispatchFunction = &DispatchToInplaceLambda<Function>;
152 }
153 else
154 {
155 m_Instance.m_ConstPtr = HeapLambda();
156 m_pLambdaStorage = PL_NEW(pAllocator, plLambdaDelegateStorage<Function>, std::move(function));
157 m_pAllocator = pAllocator;
158 memset(m_Data + 2 * sizeof(void*), 0, DataSize - 2 * sizeof(void*));
159 m_DispatchFunction = &DispatchToHeapLambda<Function>;
160 }
161 }
162 }
163
164 PL_ALWAYS_INLINE ~plDelegate() { Invalidate(); }
165
167 PL_FORCE_INLINE void operator=(const SelfType& other)
168 {
169 Invalidate();
170
171 if (other.IsHeapLambda())
172 {
173 m_pAllocator = other.m_pAllocator;
174 m_pLambdaStorage = other.m_pLambdaStorage->Clone(m_pAllocator);
175 }
176 else if (other.IsInplaceLambda())
177 {
178 auto pOtherLambdaStorage = reinterpret_cast<plLambdaDelegateStorageBase*>(&other.m_Data);
179 pOtherLambdaStorage->InplaceCopy(m_Data);
180 }
181 else
182 {
183 memcpy(m_Data, other.m_Data, DataSize);
184 }
185
186 m_Instance = other.m_Instance;
187 m_DispatchFunction = other.m_DispatchFunction;
188 }
189
191 PL_FORCE_INLINE void operator=(SelfType&& other)
192 {
193 Invalidate();
194 m_Instance = other.m_Instance;
195 m_DispatchFunction = other.m_DispatchFunction;
196
197 if (other.IsInplaceLambda())
198 {
199 auto pOtherLambdaStorage = reinterpret_cast<plLambdaDelegateStorageBase*>(&other.m_Data);
200 pOtherLambdaStorage->InplaceMove(m_Data);
201 }
202 else
203 {
204 memcpy(m_Data, other.m_Data, DataSize);
205 }
206
207 other.m_Instance.m_Ptr = nullptr;
208 other.m_DispatchFunction = nullptr;
209 memset(other.m_Data, 0, DataSize);
210 }
211
213 PL_FORCE_INLINE void operator=(std::nullptr_t) { Invalidate(); }
214
216 PL_FORCE_INLINE R operator()(Args... params) const
217 {
218 PL_ASSERT_DEBUG(m_DispatchFunction != nullptr, "Delegate is not bound.");
219 return (*m_DispatchFunction)(*this, params...);
220 }
221
223 PL_ALWAYS_INLINE bool operator==(const SelfType& other) const
224 {
225 PL_REPORT_FAILURE("operator== for plDelegate must not be used. Use IsEqualIfNotHeapAllocated() and read its documentation!");
226 return false;
227 }
228
233 PL_ALWAYS_INLINE bool IsEqualIfComparable(const SelfType& other) const
234 {
235 return m_Instance.m_Ptr == other.m_Instance.m_Ptr && m_DispatchFunction == other.m_DispatchFunction &&
236 memcmp(m_Data, other.m_Data, DataSize) == 0;
237 }
238
240 PL_ALWAYS_INLINE bool IsValid() const { return m_DispatchFunction != nullptr; }
241
243 PL_FORCE_INLINE void Invalidate()
244 {
245 m_DispatchFunction = nullptr;
246 if (IsHeapLambda())
247 {
248 PL_DELETE(m_pAllocator, m_pLambdaStorage);
249 }
250 else if (IsInplaceLambda())
251 {
252 auto pLambdaStorage = reinterpret_cast<plLambdaDelegateStorageBase*>(&m_Data);
253 pLambdaStorage->~plLambdaDelegateStorageBase();
254 }
255
256 m_Instance.m_Ptr = nullptr;
257 memset(m_Data, 0, DataSize);
258 }
259
261 PL_ALWAYS_INLINE void* GetClassInstance() const { return IsComparable() ? m_Instance.m_Ptr : nullptr; }
262
264 PL_ALWAYS_INLINE bool IsComparable() const { return m_Instance.m_ConstPtr < InplaceLambda(); } // [tested]
265
266private:
267 template <typename Function>
268 PL_FORCE_INLINE void CopyFunctionToInplaceStorage(Function function)
269 {
270 PL_ASSERT_DEBUG(
271 plMemoryUtils::IsAligned(&m_Data, PL_ALIGNMENT_OF(Function)), "Wrong alignment. Expected {0} bytes alignment", PL_ALIGNMENT_OF(Function));
272
273 memcpy(m_Data, &function, sizeof(Function));
274 memset(m_Data + sizeof(Function), 0, DataSize - sizeof(Function));
275 }
276
277 template <typename Method>
278 PL_FORCE_INLINE void CopyMemberFunctionToInplaceStorage(Method method)
279 {
280 static_assert(DataSize >= 16, "DataSize must be at least 16 bytes");
281 static_assert(sizeof(Method) <= DataSize, "Member function pointer must not be bigger than 16 bytes");
282
283 CopyFunctionToInplaceStorage(method);
284
285 // Member Function Pointers in MSVC are 12 bytes in size and have 4 byte padding
286 // MSVC builds a member function pointer on the stack writing only 12 bytes and then copies it
287 // to the final location by copying 16 bytes. Thus the 4 byte padding get a random value (whatever is on the stack at that time).
288 // To make the delegate comparable by memcmp we zero out those 4 byte padding.
289 // Apparently clang does the same on windows but not on linux etc.
290#if PL_ENABLED(PL_COMPILER_MSVC) || (PL_ENABLED(PL_PLATFORM_WINDOWS) && PL_ENABLED(PL_COMPILER_CLANG))
291 *reinterpret_cast<plUInt32*>(m_Data + 12) = 0;
292#endif
293 }
294
295 PL_ALWAYS_INLINE bool IsInplaceLambda() const { return m_Instance.m_ConstPtr == InplaceLambda(); }
296 PL_ALWAYS_INLINE bool IsHeapLambda() const { return m_Instance.m_ConstPtr == HeapLambda(); }
297
298 template <typename Method, typename Class>
299 static PL_FORCE_INLINE R DispatchToMethod(const SelfType& self, Args... params)
300 {
301 PL_ASSERT_DEBUG(self.m_Instance.m_Ptr != nullptr, "Instance must not be null.");
302 Method method = *reinterpret_cast<Method*>(&self.m_Data);
303 return (static_cast<Class*>(self.m_Instance.m_Ptr)->*method)(params...);
304 }
305
306 template <typename Method, typename Class>
307 static PL_FORCE_INLINE R DispatchToConstMethod(const SelfType& self, Args... params)
308 {
309 PL_ASSERT_DEBUG(self.m_Instance.m_ConstPtr != nullptr, "Instance must not be null.");
310 Method method = *reinterpret_cast<Method*>(&self.m_Data);
311 return (static_cast<const Class*>(self.m_Instance.m_ConstPtr)->*method)(params...);
312 }
313
314 template <typename Function>
315 static PL_ALWAYS_INLINE R DispatchToFunction(const SelfType& self, Args... params)
316 {
317 return (*reinterpret_cast<Function*>(&self.m_Data))(params...);
318 }
319
320 template <typename Function>
321 static PL_ALWAYS_INLINE R DispatchToHeapLambda(const SelfType& self, Args... params)
322 {
323 return static_cast<plLambdaDelegateStorage<Function>*>(self.m_pLambdaStorage)->m_func(params...);
324 }
325
326 template <typename Function>
327 static PL_ALWAYS_INLINE R DispatchToInplaceLambda(const SelfType& self, Args... params)
328 {
329 return reinterpret_cast<plLambdaDelegateStorage<Function>*>(&self.m_Data)->m_func(params...);
330 }
331
332 using DispatchFunction = R (*)(const SelfType&, Args...);
333 DispatchFunction m_DispatchFunction;
334
335 union
336 {
337 mutable plUInt8 m_Data[DataSize];
338 struct
339 {
340 plLambdaDelegateStorageBase* m_pLambdaStorage;
341 plAllocator* m_pAllocator;
342 };
343 };
344};
345
346template <typename T>
348{
349};
350
351template <typename Class, typename R, typename... Args>
352struct plMakeDelegateHelper<R (Class::*)(Args...)>
353{
354 using DelegateType = plDelegate<R(Args...)>;
355};
356
357template <typename Class, typename R, typename... Args>
358struct plMakeDelegateHelper<R (Class::*)(Args...) const>
359{
360 using DelegateType = plDelegate<R(Args...)>;
361};
Base class for all memory allocators.
Definition Allocator.h:23
Base class for plDelegate.
Definition Delegate.h:7
static PL_ALWAYS_INLINE plAllocator * GetDefaultAllocator()
The default allocator can be used for any kind of allocation if no alignment is required.
Definition Basics.h:82
static bool IsAligned(const T *pPtr, size_t uiAlignment)
Checks whether ptr is aligned to a memory address that is a multiple of uiAlignment.
Definition DelegateHelper_inl.h:77
PL_FORCE_INLINE plDelegate(Method method, Class *pInstance)
Constructs the delegate from a member function type and takes the class instance on which to call the...
Definition DelegateHelper_inl.h:95
PL_ALWAYS_INLINE bool IsEqualIfComparable(const SelfType &other) const
Checks whether two delegates are bound to the exact same function, including the class instance.
Definition DelegateHelper_inl.h:233
PL_ALWAYS_INLINE bool operator==(const SelfType &other) const
This function only exists to make code compile, but it will assert when used. Use IsEqualIfNotHeapAll...
Definition DelegateHelper_inl.h:223
PL_FORCE_INLINE void operator=(SelfType &&other)
Moves the data from another delegate.
Definition DelegateHelper_inl.h:191
PL_FORCE_INLINE void operator=(std::nullptr_t)
Resets a delegate to an invalid state.
Definition DelegateHelper_inl.h:213
PL_FORCE_INLINE void Invalidate()
Resets a delegate to an invalid state.
Definition DelegateHelper_inl.h:243
PL_FORCE_INLINE R operator()(Args... params) const
Function call operator. This will call the function that is bound to the delegate,...
Definition DelegateHelper_inl.h:216
PL_FORCE_INLINE void operator=(const SelfType &other)
Copies the data from another delegate.
Definition DelegateHelper_inl.h:167
PL_ALWAYS_INLINE bool IsComparable() const
Returns whether the delegate is comparable with other delegates of the same type. This is not the cas...
Definition DelegateHelper_inl.h:264
PL_FORCE_INLINE plDelegate(Method method, const Class *pInstance)
Constructs the delegate from a member function type and takes the (const) class instance on which to ...
Definition DelegateHelper_inl.h:105
PL_ALWAYS_INLINE void * GetClassInstance() const
Returns the class instance that is used to call a member function pointer on.
Definition DelegateHelper_inl.h:261
PL_ALWAYS_INLINE bool IsValid() const
Returns true when the delegate is bound to a valid non-nullptr function.
Definition DelegateHelper_inl.h:240
PL_FORCE_INLINE plDelegate(Function function, plAllocator *pAllocator=plFoundation::GetDefaultAllocator())
Constructs the delegate from a regular C function type.
Definition DelegateHelper_inl.h:115
A generic delegate class which supports static functions and member functions.
Definition Delegate.h:76
[Internal] Storage for lambdas with captures in plDelegate.
Definition DelegateHelper_inl.h:4
Definition DelegateHelper_inl.h:20
Definition DelegateHelper_inl.h:348