diff options
Diffstat (limited to 'include/EASTL/internal/function_detail.h')
-rw-r--r-- | include/EASTL/internal/function_detail.h | 673 |
1 files changed, 0 insertions, 673 deletions
diff --git a/include/EASTL/internal/function_detail.h b/include/EASTL/internal/function_detail.h deleted file mode 100644 index 3ee3667..0000000 --- a/include/EASTL/internal/function_detail.h +++ /dev/null @@ -1,673 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// Copyright (c) Electronic Arts Inc. All rights reserved. -/////////////////////////////////////////////////////////////////////////////// - -#ifndef EASTL_FUNCTION_DETAIL_H -#define EASTL_FUNCTION_DETAIL_H - -#if defined(EA_PRAGMA_ONCE_SUPPORTED) - #pragma once -#endif - -#include <EABase/eabase.h> -#include <EABase/nullptr.h> -#include <EABase/config/eacompilertraits.h> - -#include <EASTL/internal/config.h> -#include <EASTL/internal/functional_base.h> -#include <EASTL/internal/move_help.h> -#include <EASTL/internal/function_help.h> - -#include <EASTL/type_traits.h> -#include <EASTL/utility.h> -#include <EASTL/allocator.h> - -#if EASTL_RTTI_ENABLED - #include <typeinfo> -#endif - -#if EASTL_EXCEPTIONS_ENABLED - EA_DISABLE_ALL_VC_WARNINGS() - #include <new> - #include <exception> - EA_RESTORE_ALL_VC_WARNINGS() -#endif - -namespace eastl -{ - #if EASTL_EXCEPTIONS_ENABLED - class bad_function_call : public std::exception - { - public: - bad_function_call() EA_NOEXCEPT = default; - - const char* what() const EA_NOEXCEPT EA_OVERRIDE - { - return "bad function_detail call"; - } - }; - #endif - - namespace internal - { - class unused_class {}; - - union functor_storage_alignment - { - void (*unused_func_ptr)(void); - void (unused_class::*unused_func_mem_ptr)(void); - void* unused_ptr; - }; - - template <int SIZE_IN_BYTES> - struct functor_storage - { - static_assert(SIZE_IN_BYTES >= 0, "local buffer storage cannot have a negative size!"); - template <typename Ret> - Ret& GetStorageTypeRef() const - { - return *reinterpret_cast<Ret*>(const_cast<char*>(&storage[0])); - } - - union - { - functor_storage_alignment align; - char storage[SIZE_IN_BYTES]; - }; - }; - - template <> - struct functor_storage<0> - { - template <typename Ret> - Ret& GetStorageTypeRef() const - { - return *reinterpret_cast<Ret*>(const_cast<char*>(&storage[0])); - } - - union - { - functor_storage_alignment align; - char storage[sizeof(functor_storage_alignment)]; - }; - }; - - template <typename Functor, int SIZE_IN_BYTES> - struct is_functor_inplace_allocatable - { - static EA_CONSTEXPR bool value = - sizeof(Functor) <= sizeof(functor_storage<SIZE_IN_BYTES>) && - (eastl::alignment_of_v<functor_storage<SIZE_IN_BYTES>> % eastl::alignment_of_v<Functor>) == 0; - }; - - - /// function_base_detail - /// - template <int SIZE_IN_BYTES> - class function_base_detail - { - public: - using FunctorStorageType = functor_storage<SIZE_IN_BYTES>; - FunctorStorageType mStorage; - - enum ManagerOperations : int - { - MGROPS_DESTRUCT_FUNCTOR = 0, - MGROPS_COPY_FUNCTOR = 1, - MGROPS_MOVE_FUNCTOR = 2, - #if EASTL_RTTI_ENABLED - MGROPS_GET_TYPE_INFO = 3, - MGROPS_GET_FUNC_PTR = 4, - #endif - }; - - // Functor can be allocated inplace - template <typename Functor, typename = void> - class function_manager_base - { - public: - - static Functor* GetFunctorPtr(const FunctorStorageType& storage) EA_NOEXCEPT - { - return &(storage.template GetStorageTypeRef<Functor>()); - } - - template <typename T> - static void CreateFunctor(FunctorStorageType& storage, T&& functor) - { - ::new (GetFunctorPtr(storage)) Functor(eastl::forward<T>(functor)); - } - - static void DestructFunctor(FunctorStorageType& storage) - { - GetFunctorPtr(storage)->~Functor(); - } - - static void CopyFunctor(FunctorStorageType& to, const FunctorStorageType& from) - { - ::new (GetFunctorPtr(to)) Functor(*GetFunctorPtr(from)); - } - - static void MoveFunctor(FunctorStorageType& to, FunctorStorageType& from) EA_NOEXCEPT - { - ::new (GetFunctorPtr(to)) Functor(eastl::move(*GetFunctorPtr(from))); - } - - static void* Manager(void* to, void* from, typename function_base_detail::ManagerOperations ops) EA_NOEXCEPT - { - switch (ops) - { - case MGROPS_DESTRUCT_FUNCTOR: - { - DestructFunctor(*static_cast<FunctorStorageType*>(to)); - } - break; - case MGROPS_COPY_FUNCTOR: - { - CopyFunctor(*static_cast<FunctorStorageType*>(to), - *static_cast<const FunctorStorageType*>(from)); - } - break; - case MGROPS_MOVE_FUNCTOR: - { - MoveFunctor(*static_cast<FunctorStorageType*>(to), *static_cast<FunctorStorageType*>(from)); - DestructFunctor(*static_cast<FunctorStorageType*>(from)); - } - break; - default: - break; - } - return nullptr; - } - }; - - // Functor is allocated on the heap - template <typename Functor> - class function_manager_base<Functor, typename eastl::enable_if<!is_functor_inplace_allocatable<Functor, SIZE_IN_BYTES>::value>::type> - { - public: - static Functor* GetFunctorPtr(const FunctorStorageType& storage) EA_NOEXCEPT - { - return storage.template GetStorageTypeRef<Functor*>(); - } - - static Functor*& GetFunctorPtrRef(const FunctorStorageType& storage) EA_NOEXCEPT - { - return storage.template GetStorageTypeRef<Functor*>(); - } - - template <typename T> - static void CreateFunctor(FunctorStorageType& storage, T&& functor) - { - auto& allocator = *EASTLAllocatorDefault(); - Functor* func = static_cast<Functor*>(allocator.allocate(sizeof(Functor), alignof(Functor), 0)); - - #if EASTL_EXCEPTIONS_ENABLED - if (!func) - { - throw std::bad_alloc(); - } - #else - EASTL_ASSERT_MSG(func != nullptr, "Allocation failed!"); - #endif - - ::new (static_cast<void*>(func)) Functor(eastl::forward<T>(functor)); - GetFunctorPtrRef(storage) = func; - } - - static void DestructFunctor(FunctorStorageType& storage) - { - Functor* func = GetFunctorPtr(storage); - if (func) - { - auto& allocator = *EASTLAllocatorDefault(); - func->~Functor(); - allocator.deallocate(static_cast<void*>(func), sizeof(Functor)); - } - } - - static void CopyFunctor(FunctorStorageType& to, const FunctorStorageType& from) - { - auto& allocator = *EASTLAllocatorDefault(); - Functor* func = static_cast<Functor*>(allocator.allocate(sizeof(Functor), alignof(Functor), 0)); - #if EASTL_EXCEPTIONS_ENABLED - if (!func) - { - throw std::bad_alloc(); - } - #else - EASTL_ASSERT_MSG(func != nullptr, "Allocation failed!"); - #endif - ::new (static_cast<void*>(func)) Functor(*GetFunctorPtr(from)); - GetFunctorPtrRef(to) = func; - } - - static void MoveFunctor(FunctorStorageType& to, FunctorStorageType& from) EA_NOEXCEPT - { - Functor* func = GetFunctorPtr(from); - GetFunctorPtrRef(to) = func; - GetFunctorPtrRef(from) = nullptr; - } - - static void* Manager(void* to, void* from, typename function_base_detail::ManagerOperations ops) EA_NOEXCEPT - { - switch (ops) - { - case MGROPS_DESTRUCT_FUNCTOR: - { - DestructFunctor(*static_cast<FunctorStorageType*>(to)); - } - break; - case MGROPS_COPY_FUNCTOR: - { - CopyFunctor(*static_cast<FunctorStorageType*>(to), - *static_cast<const FunctorStorageType*>(from)); - } - break; - case MGROPS_MOVE_FUNCTOR: - { - MoveFunctor(*static_cast<FunctorStorageType*>(to), *static_cast<FunctorStorageType*>(from)); - // Moved ptr, no need to destruct ourselves - } - break; - default: - break; - } - return nullptr; - } - }; - - template <typename Functor, typename R, typename... Args> - class function_manager final : public function_manager_base<Functor> - { - public: - using Base = function_manager_base<Functor>; - - #if EASTL_RTTI_ENABLED - static void* GetTypeInfo() EA_NOEXCEPT - { - return reinterpret_cast<void*>(const_cast<std::type_info*>(&typeid(Functor))); - } - - static void* Manager(void* to, void* from, typename function_base_detail::ManagerOperations ops) EA_NOEXCEPT - { - switch (ops) - { - case MGROPS_GET_TYPE_INFO: - { - return GetTypeInfo(); - } - break; - case MGROPS_GET_FUNC_PTR: - { - return static_cast<void*>(Base::GetFunctorPtr(*static_cast<FunctorStorageType*>(to))); - } - break; - default: - { - return Base::Manager(to, from, ops); - } - break; - } - } - #endif // EASTL_RTTI_ENABLED - - /** - * NOTE: - * - * The order of arguments here is vital to the call optimization. Let's dig into why and look at some asm. - * We have two invoker signatures to consider: - * R Invoker(const FunctorStorageType& functor, Args... args) - * R Invoker(Args... args, const FunctorStorageType& functor) - * - * Assume we are using the Windows x64 Calling Convention where the first 4 arguments are passed into - * RCX, RDX, R8, R9. This optimization works for any Calling Convention, we are just using Windows x64 for - * this example. - * - * Given the following member function: void TestMemberFunc(int a, int b) - * RCX == this - * RDX == a - * R8 == b - * - * All three arguments to the function including the hidden this pointer, which in C++ is always the first argument - * are passed into the first three registers. - * The function call chain for eastl::function<>() is as follows: - * operator ()(this, Args... args) -> Invoker(Args... args, this->mStorage) -> StoredFunction(Args... arg) - * - * Let's look at what is happening at the asm level with the different Invoker function signatures and why. - * - * You will notice that operator ()() and Invoker() have the arguments reversed. operator ()() just directly calls - * to Invoker(), it is a tail call, so we force inline the call operator to ensure we directly call to the Invoker(). - * Most compilers always inline it anyways by default; have been instances where it doesn't even though the asm ends - * up being cheaper. - * call -> call -> call versus call -> call - * - * eastl::function<int(int, int)> = FunctionPointer - * - * Assume we have the above eastl::function object that holds a pointer to a function as the internal callable. - * - * Invoker(this->mStorage, Args... args) is called with the follow arguments in registers: - * RCX = this | RDX = a | R8 = b - * - * Inside Invoker() we use RCX to deference into the eastl::function object and get the function pointer to call. - * This function to call has signature Func(int, int) and thus requires its arguments in registers RCX and RDX. - * The compiler must shift all the arguments towards the left. The full asm looks something as follows. - * - * Calling Invoker: Inside Invoker: - * - * mov rcx, this mov rax, [rcx] - * mov rdx, a mov rcx, rdx - * mov r8, b mov rdx, r8 - * call [rcx + offset to Invoker] jmp [rax] - * - * Notice how the compiler shifts all the arguments before calling the callable and also we only use the this pointer - * to access the internal storage inside the eastl::function object. - * - * Invoker(Args... args, this->mStorage) is called with the following arguments in registers: - * RCX = a | RDX = b | R8 = this - * - * You can see we no longer have to shift the arguments down when going to call the internal stored callable. - * - * Calling Invoker: Inside Invoker: - * - * mov rcx, a mov rax, [r8] - * mov rdx, b jmp [rax] - * mov r8, this - * call [r8 + offset to Invoker] - * - * The generated asm does a straight tail jmp to the loaded function pointer. The arguments are already in the correct - * registers. - * - * For Functors or Lambdas with no captures, this gives us another free register to use to pass arguments since the this - * is at the end, it can be passed onto the stack if we run out of registers. Since the callable has no captures; inside - * the Invoker(), we won't ever need to touch this thus we can just call the operator ()() or let the compiler inline it. - * - * For a callable with captures there is no perf hit since the callable in the common case is inlined and the pointer to the callable - * buffer is passed in a register which the compiler can use to access the captures. - * - * For eastl::function<void(const T&, int, int)> that a holds a pointer to member function. The this pointers is implicitly - * the first argument in the argument list, const T&, and the member function pointer will be called on that object. - * This prevents any argument shifting since the this for the member function pointer is already in RCX. - * - * This is why having this at the end of the argument list is important for generating efficient Invoker() thunks. - */ - static R Invoker(Args... args, const FunctorStorageType& functor) - { - return eastl::invoke(*Base::GetFunctorPtr(functor), eastl::forward<Args>(args)...); - } - }; - - function_base_detail() EA_NOEXCEPT = default; - ~function_base_detail() EA_NOEXCEPT = default; - }; - - #define EASTL_INTERNAL_FUNCTION_VALID_FUNCTION_ARGS(FUNCTOR, RET, ARGS, BASE, MYSELF) \ - typename eastl::enable_if_t<eastl::is_invocable_r_v<RET, FUNCTOR, ARGS> && \ - !eastl::is_base_of_v<BASE, eastl::decay_t<FUNCTOR>> && \ - !eastl::is_same_v<eastl::decay_t<FUNCTOR>, MYSELF>> - - #define EASTL_INTERNAL_FUNCTION_DETAIL_VALID_FUNCTION_ARGS(FUNCTOR, RET, ARGS, MYSELF) \ - EASTL_INTERNAL_FUNCTION_VALID_FUNCTION_ARGS(FUNCTOR, RET, ARGS, MYSELF, MYSELF) - - - /// function_detail - /// - template <int, typename> - class function_detail; - - template <int SIZE_IN_BYTES, typename R, typename... Args> - class function_detail<SIZE_IN_BYTES, R(Args...)> : public function_base_detail<SIZE_IN_BYTES> - { - public: - using result_type = R; - - protected: - using Base = function_base_detail<SIZE_IN_BYTES>; - using FunctorStorageType = typename function_base_detail<SIZE_IN_BYTES>::FunctorStorageType; - using Base::mStorage; - - public: - function_detail() EA_NOEXCEPT = default; - function_detail(std::nullptr_t) EA_NOEXCEPT {} - - function_detail(const function_detail& other) - { - if (this != &other) - { - Copy(other); - } - } - - function_detail(function_detail&& other) - { - if (this != &other) - { - Move(eastl::move(other)); - } - } - - template <typename Functor, typename = EASTL_INTERNAL_FUNCTION_DETAIL_VALID_FUNCTION_ARGS(Functor, R, Args..., function_detail)> - function_detail(Functor functor) - { - CreateForwardFunctor(eastl::move(functor)); - } - - ~function_detail() EA_NOEXCEPT - { - Destroy(); - } - - function_detail& operator=(const function_detail& other) - { - if (this != &other) - { - Destroy(); - Copy(other); - } - - return *this; - } - - function_detail& operator=(function_detail&& other) - { - if(this != &other) - { - Destroy(); - Move(eastl::move(other)); - } - - return *this; - } - - function_detail& operator=(std::nullptr_t) EA_NOEXCEPT - { - Destroy(); - mMgrFuncPtr = nullptr; - mInvokeFuncPtr = &DefaultInvoker; - - return *this; - } - - template <typename Functor, typename = EASTL_INTERNAL_FUNCTION_DETAIL_VALID_FUNCTION_ARGS(Functor, R, Args..., function_detail)> - function_detail& operator=(Functor&& functor) - { - Destroy(); - CreateForwardFunctor(eastl::forward<Functor>(functor)); - return *this; - } - - template <typename Functor> - function_detail& operator=(eastl::reference_wrapper<Functor> f) EA_NOEXCEPT - { - Destroy(); - CreateForwardFunctor(f); - return *this; - } - - void swap(function_detail& other) EA_NOEXCEPT - { - if(this == &other) - return; - - FunctorStorageType tempStorage; - if (other.HaveManager()) - { - (void)(*other.mMgrFuncPtr)(static_cast<void*>(&tempStorage), static_cast<void*>(&other.mStorage), - Base::ManagerOperations::MGROPS_MOVE_FUNCTOR); - } - - if (HaveManager()) - { - (void)(*mMgrFuncPtr)(static_cast<void*>(&other.mStorage), static_cast<void*>(&mStorage), - Base::ManagerOperations::MGROPS_MOVE_FUNCTOR); - } - - if (other.HaveManager()) - { - (void)(*other.mMgrFuncPtr)(static_cast<void*>(&mStorage), static_cast<void*>(&tempStorage), - Base::ManagerOperations::MGROPS_MOVE_FUNCTOR); - } - - eastl::swap(mMgrFuncPtr, other.mMgrFuncPtr); - eastl::swap(mInvokeFuncPtr, other.mInvokeFuncPtr); - } - - explicit operator bool() const EA_NOEXCEPT - { - return HaveManager(); - } - - EASTL_FORCE_INLINE R operator ()(Args... args) const - { - return (*mInvokeFuncPtr)(eastl::forward<Args>(args)..., this->mStorage); - } - - #if EASTL_RTTI_ENABLED - const std::type_info& target_type() const EA_NOEXCEPT - { - if (HaveManager()) - { - void* ret = (*mMgrFuncPtr)(nullptr, nullptr, Base::ManagerOperations::MGROPS_GET_TYPE_INFO); - return *(static_cast<const std::type_info*>(ret)); - } - return typeid(void); - } - - template <typename Functor> - Functor* target() EA_NOEXCEPT - { - if (HaveManager() && target_type() == typeid(Functor)) - { - void* ret = (*mMgrFuncPtr)(static_cast<void*>(&mStorage), nullptr, - Base::ManagerOperations::MGROPS_GET_FUNC_PTR); - return ret ? static_cast<Functor*>(ret) : nullptr; - } - return nullptr; - } - - template <typename Functor> - const Functor* target() const EA_NOEXCEPT - { - if (HaveManager() && target_type() == typeid(Functor)) - { - void* ret = (*mMgrFuncPtr)(static_cast<void*>(&mStorage), nullptr, - Base::ManagerOperations::MGROPS_GET_FUNC_PTR); - return ret ? static_cast<const Functor*>(ret) : nullptr; - } - return nullptr; - } - #endif // EASTL_RTTI_ENABLED - - private: - bool HaveManager() const EA_NOEXCEPT - { - return (mMgrFuncPtr != nullptr); - } - - void Destroy() EA_NOEXCEPT - { - if (HaveManager()) - { - (void)(*mMgrFuncPtr)(static_cast<void*>(&mStorage), nullptr, - Base::ManagerOperations::MGROPS_DESTRUCT_FUNCTOR); - } - } - - void Copy(const function_detail& other) - { - if (other.HaveManager()) - { - (void)(*other.mMgrFuncPtr)(static_cast<void*>(&mStorage), - const_cast<void*>(static_cast<const void*>(&other.mStorage)), - Base::ManagerOperations::MGROPS_COPY_FUNCTOR); - } - - mMgrFuncPtr = other.mMgrFuncPtr; - mInvokeFuncPtr = other.mInvokeFuncPtr; - } - - void Move(function_detail&& other) - { - if (other.HaveManager()) - { - (void)(*other.mMgrFuncPtr)(static_cast<void*>(&mStorage), static_cast<void*>(&other.mStorage), - Base::ManagerOperations::MGROPS_MOVE_FUNCTOR); - } - - mMgrFuncPtr = other.mMgrFuncPtr; - mInvokeFuncPtr = other.mInvokeFuncPtr; - other.mMgrFuncPtr = nullptr; - other.mInvokeFuncPtr = &DefaultInvoker; - } - - template <typename Functor> - void CreateForwardFunctor(Functor&& functor) - { - using DecayedFunctorType = typename eastl::decay<Functor>::type; - using FunctionManagerType = typename Base::template function_manager<DecayedFunctorType, R, Args...>; - - if (internal::is_null(functor)) - { - mMgrFuncPtr = nullptr; - mInvokeFuncPtr = &DefaultInvoker; - } - else - { - mMgrFuncPtr = &FunctionManagerType::Manager; - mInvokeFuncPtr = &FunctionManagerType::Invoker; - FunctionManagerType::CreateFunctor(mStorage, eastl::forward<Functor>(functor)); - } - } - - private: - typedef void* (*ManagerFuncPtr)(void*, void*, typename Base::ManagerOperations); - typedef R (*InvokeFuncPtr)(Args..., const FunctorStorageType&); - - EA_DISABLE_GCC_WARNING(-Wreturn-type); - EA_DISABLE_CLANG_WARNING(-Wreturn-type); - EA_DISABLE_VC_WARNING(4716); // 'function' must return a value - // We cannot assume that R is default constructible. - // This function is called only when the function object CANNOT be called because it is empty, - // it will always throw or assert so we never use the return value anyways and neither should the caller. - static R DefaultInvoker(Args... /*args*/, const FunctorStorageType& /*functor*/) - { - #if EASTL_EXCEPTIONS_ENABLED - throw eastl::bad_function_call(); - #else - EASTL_ASSERT_MSG(false, "function_detail call on an empty function_detail<R(Args..)>"); - #endif - }; - EA_RESTORE_VC_WARNING(); - EA_RESTORE_CLANG_WARNING(); - EA_RESTORE_GCC_WARNING(); - - - ManagerFuncPtr mMgrFuncPtr = nullptr; - InvokeFuncPtr mInvokeFuncPtr = &DefaultInvoker; - }; - - } // namespace internal - -} // namespace eastl - -#endif // EASTL_FUNCTION_DETAIL_H |