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