1 ///////////////////////////////////////////////////////////////////////////////
2 // Copyright (c) Electronic Arts Inc. All rights reserved.
3 ///////////////////////////////////////////////////////////////////////////////
4 
5 #ifndef EASTL_FUNCTION_DETAIL_H
6 #define EASTL_FUNCTION_DETAIL_H
7 
8 #if defined(EA_PRAGMA_ONCE_SUPPORTED)
9 	#pragma once
10 #endif
11 
12 #include <EABase/eabase.h>
13 #include <EABase/nullptr.h>
14 #include <EABase/config/eacompilertraits.h>
15 
16 #include <EASTL/internal/config.h>
17 #include <EASTL/internal/functional_base.h>
18 #include <EASTL/internal/move_help.h>
19 #include <EASTL/internal/function_help.h>
20 #include <EASTL/internal/allocator_traits_fwd_decls.h>
21 
22 #include <EASTL/type_traits.h>
23 #include <EASTL/utility.h>
24 #include <EASTL/allocator.h>
25 
26 #if EASTL_RTTI_ENABLED
27 	#include <typeinfo>
28 #endif
29 
30 #if EASTL_EXCEPTIONS_ENABLED
31 	EA_DISABLE_ALL_VC_WARNINGS()
32 	#include <new>
33 	#include <exception>
EA_RESTORE_ALL_VC_WARNINGS()34 	EA_RESTORE_ALL_VC_WARNINGS()
35 #endif
36 
37 namespace eastl
38 {
39 	#if EASTL_EXCEPTIONS_ENABLED
40 		class bad_function_call : public std::exception
41 		{
42 		public:
43 			bad_function_call() EA_NOEXCEPT = default;
44 
45 			const char* what() const EA_NOEXCEPT EA_OVERRIDE
46 			{
47 				return "bad function_detail call";
48 			}
49 		};
50 	#endif
51 
52 	namespace internal
53 	{
54 		class unused_class {};
55 
56 		union functor_storage_alignment
57 		{
58 			void (*unused_func_ptr)(void);
59 			void (unused_class::*unused_func_mem_ptr)(void);
60 			void* unused_ptr;
61 		};
62 
63 		template <int SIZE_IN_BYTES>
64 		struct functor_storage
65 		{
66 			static_assert(SIZE_IN_BYTES >= 0, "local buffer storage cannot have a negative size!");
67 			template <typename Ret>
68 			Ret& GetStorageTypeRef() const
69 			{
70 				return *reinterpret_cast<Ret*>(const_cast<char*>(&storage[0]));
71 			}
72 
73 			union
74 			{
75 				functor_storage_alignment align;
76 				char storage[SIZE_IN_BYTES];
77 			};
78 		};
79 
80 		template <>
81 		struct functor_storage<0>
82 		{
83 			template <typename Ret>
84 			Ret& GetStorageTypeRef() const
85 			{
86 				return *reinterpret_cast<Ret*>(const_cast<char*>(&storage[0]));
87 			}
88 
89 			union
90 			{
91 				functor_storage_alignment align;
92 				char storage[sizeof(functor_storage_alignment)];
93 			};
94 		};
95 
96 		template <typename Functor, int SIZE_IN_BYTES>
97 		struct is_functor_inplace_allocatable
98 		{
99 			static constexpr bool value =
100 			    sizeof(Functor) <= sizeof(functor_storage<SIZE_IN_BYTES>) &&
101 			    (eastl::alignment_of_v<functor_storage<SIZE_IN_BYTES>> % eastl::alignment_of_v<Functor>) == 0;
102 		};
103 
104 
105 		/// function_base_detail
106 		///
107 		template <int SIZE_IN_BYTES>
108 		class function_base_detail
109 		{
110 		public:
111 			using FunctorStorageType = functor_storage<SIZE_IN_BYTES>;
112 			FunctorStorageType mStorage;
113 
114 			enum ManagerOperations : int
115 			{
116 				MGROPS_DESTRUCT_FUNCTOR = 0,
117 				MGROPS_COPY_FUNCTOR = 1,
118 				MGROPS_MOVE_FUNCTOR = 2,
119 			#if EASTL_RTTI_ENABLED
120 				MGROPS_GET_TYPE_INFO = 3,
121 				MGROPS_GET_FUNC_PTR = 4,
122 			#endif
123 			};
124 
125 			// Functor can be allocated inplace
126 			template <typename Functor, typename = void>
127 			class function_manager_base
128 			{
129 			public:
130 
131 				static Functor* GetFunctorPtr(const FunctorStorageType& storage) EA_NOEXCEPT
132 				{
133 					return &(storage.template GetStorageTypeRef<Functor>());
134 				}
135 
136 				template <typename T>
137 				static void CreateFunctor(FunctorStorageType& storage, T&& functor)
138 				{
139 					::new (GetFunctorPtr(storage)) Functor(eastl::forward<T>(functor));
140 				}
141 
142 				static void DestructFunctor(FunctorStorageType& storage)
143 				{
144 					GetFunctorPtr(storage)->~Functor();
145 				}
146 
147 				static void CopyFunctor(FunctorStorageType& to, const FunctorStorageType& from)
148 				{
149 					::new (GetFunctorPtr(to)) Functor(*GetFunctorPtr(from));
150 				}
151 
152 				static void MoveFunctor(FunctorStorageType& to, FunctorStorageType& from) EA_NOEXCEPT
153 				{
154 					::new (GetFunctorPtr(to)) Functor(eastl::move(*GetFunctorPtr(from)));
155 				}
156 
157 				static void* Manager(void* to, void* from, typename function_base_detail::ManagerOperations ops) EA_NOEXCEPT
158 				{
159 					switch (ops)
160 					{
161 						case MGROPS_DESTRUCT_FUNCTOR:
162 						{
163 							DestructFunctor(*static_cast<FunctorStorageType*>(to));
164 						}
165 						break;
166 						case MGROPS_COPY_FUNCTOR:
167 						{
168 							CopyFunctor(*static_cast<FunctorStorageType*>(to),
169 							            *static_cast<const FunctorStorageType*>(from));
170 						}
171 						break;
172 						case MGROPS_MOVE_FUNCTOR:
173 						{
174 							MoveFunctor(*static_cast<FunctorStorageType*>(to), *static_cast<FunctorStorageType*>(from));
175 							DestructFunctor(*static_cast<FunctorStorageType*>(from));
176 						}
177 						break;
178 						default:
179 							break;
180 					}
181 					return nullptr;
182 				}
183 			};
184 
185 			// Functor is allocated on the heap
186 			template <typename Functor>
187 			class function_manager_base<Functor, typename eastl::enable_if<!is_functor_inplace_allocatable<Functor, SIZE_IN_BYTES>::value>::type>
188 			{
189 			public:
190 				static Functor* GetFunctorPtr(const FunctorStorageType& storage) EA_NOEXCEPT
191 				{
192 					return storage.template GetStorageTypeRef<Functor*>();
193 				}
194 
195 				static Functor*& GetFunctorPtrRef(const FunctorStorageType& storage) EA_NOEXCEPT
196 				{
197 					return storage.template GetStorageTypeRef<Functor*>();
198 				}
199 
200 				template <typename T>
201 				static void CreateFunctor(FunctorStorageType& storage, T&& functor)
202 				{
203 					auto& allocator = *EASTLAllocatorDefault();
204 					Functor* func = static_cast<Functor*>(allocator.allocate(sizeof(Functor), alignof(Functor), 0));
205 
206 				#if EASTL_EXCEPTIONS_ENABLED
207 					if (!func)
208 					{
209 						throw std::bad_alloc();
210 					}
211 				#else
212 					EASTL_ASSERT_MSG(func != nullptr, "Allocation failed!");
213 				#endif
214 
215 					::new (static_cast<void*>(func)) Functor(eastl::forward<T>(functor));
216 					GetFunctorPtrRef(storage) = func;
217 				}
218 
219 				static void DestructFunctor(FunctorStorageType& storage)
220 				{
221 					Functor* func = GetFunctorPtr(storage);
222 					if (func)
223 					{
224 						auto& allocator = *EASTLAllocatorDefault();
225 						func->~Functor();
226 						allocator.deallocate(static_cast<void*>(func), sizeof(Functor));
227 					}
228 				}
229 
230 				static void CopyFunctor(FunctorStorageType& to, const FunctorStorageType& from)
231 				{
232 					auto& allocator = *EASTLAllocatorDefault();
233 					Functor* func = static_cast<Functor*>(allocator.allocate(sizeof(Functor), alignof(Functor), 0));
234 				#if EASTL_EXCEPTIONS_ENABLED
235 					if (!func)
236 					{
237 						throw std::bad_alloc();
238 					}
239 				#else
240 					EASTL_ASSERT_MSG(func != nullptr, "Allocation failed!");
241 				#endif
242 					::new (static_cast<void*>(func)) Functor(*GetFunctorPtr(from));
243 					GetFunctorPtrRef(to) = func;
244 				}
245 
246 				static void MoveFunctor(FunctorStorageType& to, FunctorStorageType& from) EA_NOEXCEPT
247 				{
248 					Functor* func = GetFunctorPtr(from);
249 					GetFunctorPtrRef(to) = func;
250 					GetFunctorPtrRef(from) = nullptr;
251 				}
252 
253 				static void* Manager(void* to, void* from, typename function_base_detail::ManagerOperations ops) EA_NOEXCEPT
254 				{
255 					switch (ops)
256 					{
257 						case MGROPS_DESTRUCT_FUNCTOR:
258 						{
259 							DestructFunctor(*static_cast<FunctorStorageType*>(to));
260 						}
261 						break;
262 						case MGROPS_COPY_FUNCTOR:
263 						{
264 							CopyFunctor(*static_cast<FunctorStorageType*>(to),
265 							            *static_cast<const FunctorStorageType*>(from));
266 						}
267 						break;
268 						case MGROPS_MOVE_FUNCTOR:
269 						{
270 							MoveFunctor(*static_cast<FunctorStorageType*>(to), *static_cast<FunctorStorageType*>(from));
271 							// Moved ptr, no need to destruct ourselves
272 						}
273 						break;
274 						default:
275 							break;
276 					}
277 					return nullptr;
278 				}
279 			};
280 
281 			template <typename Functor, typename R, typename... Args>
282 			class function_manager final : public function_manager_base<Functor>
283 			{
284 			public:
285 				using Base = function_manager_base<Functor>;
286 
287 			#if EASTL_RTTI_ENABLED
288 				static void* GetTypeInfo() EA_NOEXCEPT
289 				{
290 					return reinterpret_cast<void*>(const_cast<std::type_info*>(&typeid(Functor)));
291 				}
292 
293 				static void* Manager(void* to, void* from, typename function_base_detail::ManagerOperations ops) EA_NOEXCEPT
294 				{
295 					switch (ops)
296 					{
297 						case MGROPS_GET_TYPE_INFO:
298 						{
299 							return GetTypeInfo();
300 						}
301 						break;
302 						case MGROPS_GET_FUNC_PTR:
303 						{
304 							return static_cast<void*>(Base::GetFunctorPtr(*static_cast<FunctorStorageType*>(to)));
305 						}
306 						break;
307 						default:
308 						{
309 							return Base::Manager(to, from, ops);
310 						}
311 						break;
312 					}
313 				}
314 			#endif // EASTL_RTTI_ENABLED
315 
316 				static R Invoker(const FunctorStorageType& functor, Args... args)
317 				{
318 					return eastl::invoke(*Base::GetFunctorPtr(functor), eastl::forward<Args>(args)...);
319 				}
320 			};
321 
322 			function_base_detail() EA_NOEXCEPT = default;
323 			~function_base_detail() EA_NOEXCEPT = default;
324 		};
325 
326 		#define EASTL_INTERNAL_FUNCTION_VALID_FUNCTION_ARGS(FUNCTOR, RET, ARGS, BASE, MYSELF) \
327 			typename eastl::enable_if<eastl::is_invocable_r<RET, FUNCTOR, ARGS>::value \
328 									  && !eastl::is_base_of_v<BASE, eastl::decay_t<FUNCTOR>> \
329 									  && !eastl::is_same_v<eastl::decay_t<FUNCTOR>, MYSELF>>::type
330 
331 		#define EASTL_INTERNAL_FUNCTION_DETAIL_VALID_FUNCTION_ARGS(FUNCTOR, RET, ARGS, MYSELF) \
332 			EASTL_INTERNAL_FUNCTION_VALID_FUNCTION_ARGS(FUNCTOR, RET, ARGS, MYSELF, MYSELF)
333 
334 
335 		/// function_detail
336 		///
337 		template <int, typename>
338 		class function_detail;
339 
340 		template <int SIZE_IN_BYTES, typename R, typename... Args>
341 		class function_detail<SIZE_IN_BYTES, R(Args...)> : public function_base_detail<SIZE_IN_BYTES>
342 		{
343 		public:
344 			using result_type = R;
345 
346 		protected:
347 			using Base = function_base_detail<SIZE_IN_BYTES>;
348 			using FunctorStorageType = typename function_base_detail<SIZE_IN_BYTES>::FunctorStorageType;
349 			using Base::mStorage;
350 
351 		public:
352 			function_detail() EA_NOEXCEPT = default;
353 			function_detail(std::nullptr_t) EA_NOEXCEPT {}
354 
355 			function_detail(const function_detail& other)
356 			{
357 				if (this != &other)
358 				{
359 					Copy(other);
360 				}
361 			}
362 
363 			function_detail(function_detail&& other)
364 			{
365 				if (this != &other)
366 				{
367 					Move(eastl::move(other));
368 				}
369 			}
370 
371 			template <typename Functor, typename = EASTL_INTERNAL_FUNCTION_DETAIL_VALID_FUNCTION_ARGS(Functor, R, Args..., function_detail)>
372 			function_detail(Functor functor)
373 			{
374 				CreateForwardFunctor(eastl::move(functor));
375 			}
376 
377 			~function_detail() EA_NOEXCEPT
378 			{
379 				Destroy();
380 			}
381 
382 			function_detail& operator=(const function_detail& other)
383 			{
384 				if (this != &other)
385 				{
386 					Destroy();
387 					Copy(other);
388 				}
389 
390 				return *this;
391 			}
392 
393 			function_detail& operator=(function_detail&& other)
394 			{
395 				if(this != &other)
396 				{
397 					Destroy();
398 					Move(eastl::move(other));
399 				}
400 
401 				return *this;
402 			}
403 
404 			function_detail& operator=(std::nullptr_t) EA_NOEXCEPT
405 			{
406 				Destroy();
407 				mMgrFuncPtr = nullptr;
408 				mInvokeFuncPtr = nullptr;
409 
410 				return *this;
411 			}
412 
413 			template <typename Functor, typename = EASTL_INTERNAL_FUNCTION_DETAIL_VALID_FUNCTION_ARGS(Functor, R, Args..., function_detail)>
414 			function_detail& operator=(Functor&& functor)
415 			{
416 				Destroy();
417 				CreateForwardFunctor(eastl::forward<Functor>(functor));
418 				return *this;
419 			}
420 
421 			template <typename Functor>
422 			function_detail& operator=(eastl::reference_wrapper<Functor> f) EA_NOEXCEPT
423 			{
424 				Destroy();
425 				CreateForwardFunctor(f);
426 				return *this;
427 			}
428 
429 			void swap(function_detail& other) EA_NOEXCEPT
430 			{
431 				if(this == &other)
432 					return;
433 
434 				FunctorStorageType tempStorage;
435 				if (other.HaveManager())
436 				{
437 					(void)(*other.mMgrFuncPtr)(static_cast<void*>(&tempStorage), static_cast<void*>(&other.mStorage),
438 											   Base::ManagerOperations::MGROPS_MOVE_FUNCTOR);
439 				}
440 
441 				if (HaveManager())
442 				{
443 					(void)(*mMgrFuncPtr)(static_cast<void*>(&other.mStorage), static_cast<void*>(&mStorage),
444 										 Base::ManagerOperations::MGROPS_MOVE_FUNCTOR);
445 				}
446 
447 				if (other.HaveManager())
448 				{
449 					(void)(*other.mMgrFuncPtr)(static_cast<void*>(&mStorage), static_cast<void*>(&tempStorage),
450 											   Base::ManagerOperations::MGROPS_MOVE_FUNCTOR);
451 				}
452 
453 				eastl::swap(mMgrFuncPtr, other.mMgrFuncPtr);
454 				eastl::swap(mInvokeFuncPtr, other.mInvokeFuncPtr);
455 			}
456 
457 			explicit operator bool() const EA_NOEXCEPT
458 			{
459 				return HaveManager();
460 			}
461 
462 			R operator ()(Args... args) const
463 			{
464 			#if EASTL_EXCEPTIONS_ENABLED
465 				if (!HaveManager())
466 				{
467 					throw eastl::bad_function_call();
468 				}
469 			#else
470 				EASTL_ASSERT_MSG(HaveManager(), "function_detail call on an empty function_detail<R(Args..)>");
471 			#endif
472 				return (*mInvokeFuncPtr)(mStorage, eastl::forward<Args>(args)...);
473 			}
474 
475 			#if EASTL_RTTI_ENABLED
476 				const std::type_info& target_type() const EA_NOEXCEPT
477 				{
478 					if (HaveManager())
479 					{
480 						void* ret = (*mMgrFuncPtr)(nullptr, nullptr, Base::ManagerOperations::MGROPS_GET_TYPE_INFO);
481 						return *(static_cast<const std::type_info*>(ret));
482 					}
483 					return typeid(void);
484 				}
485 
486 				template <typename Functor>
487 				Functor* target() EA_NOEXCEPT
488 				{
489 					if (HaveManager() && target_type() == typeid(Functor))
490 					{
491 						void* ret = (*mMgrFuncPtr)(static_cast<void*>(&mStorage), nullptr,
492 												   Base::ManagerOperations::MGROPS_GET_FUNC_PTR);
493 						return ret ? static_cast<Functor*>(ret) : nullptr;
494 					}
495 					return nullptr;
496 				}
497 
498 				template <typename Functor>
499 				const Functor* target() const EA_NOEXCEPT
500 				{
501 					if (HaveManager() && target_type() == typeid(Functor))
502 					{
503 						void* ret = (*mMgrFuncPtr)(static_cast<void*>(&mStorage), nullptr,
504 												   Base::ManagerOperations::MGROPS_GET_FUNC_PTR);
505 						return ret ? static_cast<const Functor*>(ret) : nullptr;
506 					}
507 					return nullptr;
508 				}
509 			#endif // EASTL_RTTI_ENABLED
510 
511 		private:
512 			bool HaveManager() const EA_NOEXCEPT
513 			{
514 				return (mMgrFuncPtr != nullptr);
515 			}
516 
517 			void Destroy() EA_NOEXCEPT
518 			{
519 				if (HaveManager())
520 				{
521 					(void)(*mMgrFuncPtr)(static_cast<void*>(&mStorage), nullptr,
522 					                     Base::ManagerOperations::MGROPS_DESTRUCT_FUNCTOR);
523 				}
524 			}
525 
526 			void Copy(const function_detail& other)
527 			{
528 				if (other.HaveManager())
529 				{
530 					(void)(*other.mMgrFuncPtr)(static_cast<void*>(&mStorage),
531 					                           const_cast<void*>(static_cast<const void*>(&other.mStorage)),
532 					                           Base::ManagerOperations::MGROPS_COPY_FUNCTOR);
533 				}
534 
535 				mMgrFuncPtr = other.mMgrFuncPtr;
536 				mInvokeFuncPtr = other.mInvokeFuncPtr;
537 			}
538 
539 			void Move(function_detail&& other)
540 			{
541 				if (other.HaveManager())
542 				{
543 					(void)(*other.mMgrFuncPtr)(static_cast<void*>(&mStorage), static_cast<void*>(&other.mStorage),
544 					                           Base::ManagerOperations::MGROPS_MOVE_FUNCTOR);
545 				}
546 
547 				mMgrFuncPtr = other.mMgrFuncPtr;
548 				mInvokeFuncPtr = other.mInvokeFuncPtr;
549 				other.mMgrFuncPtr = nullptr;
550 				other.mInvokeFuncPtr = nullptr;
551 			}
552 
553 			template <typename Functor>
554 			void CreateForwardFunctor(Functor&& functor)
555 			{
556 				using DecayedFunctorType = typename eastl::decay<Functor>::type;
557 				using FunctionManagerType = typename Base::template function_manager<DecayedFunctorType, R, Args...>;
558 
559 				if (internal::is_null(functor))
560 				{
561 					mMgrFuncPtr = nullptr;
562 					mInvokeFuncPtr = nullptr;
563 				}
564 				else
565 				{
566 					mMgrFuncPtr = &FunctionManagerType::Manager;
567 					mInvokeFuncPtr = &FunctionManagerType::Invoker;
568 					FunctionManagerType::CreateFunctor(mStorage, eastl::forward<Functor>(functor));
569 				}
570 			}
571 
572 		private:
573 			typedef void* (*ManagerFuncPtr)(void*, void*, typename Base::ManagerOperations);
574 			typedef R (*InvokeFuncPtr)(const FunctorStorageType&, Args...);
575 
576 			ManagerFuncPtr mMgrFuncPtr = nullptr;
577 			InvokeFuncPtr mInvokeFuncPtr = nullptr;
578 		};
579 
580 	} // namespace internal
581 
582 } // namespace eastl
583 
584 #endif // EASTL_FUNCTION_DETAIL_H
585