// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "Templates/Function.h" #include "Templates/Requires.h" #include "Templates/SharedPointer.h" #include "Templates/UnrealTemplate.h" #include #if !(UE_BUILD_SHIPPING || UE_BUILD_TEST) #define METASOUND_FRONTEND_ACCESSPTR_DEBUG_INFO 1 #else #define METASOUND_FRONTEND_ACCESSPTR_DEBUG_INFO 0 #endif namespace Metasound { namespace Frontend { class FAccessPoint; /** The access token mirrors the lifespan of a object in a non-intrusive * manner. A TAccessPtr<> can be created to reference an object, but use * an access token to determine whether the referenced object is accessible. * When the access token is destroyed, the access pointer becomes invalid. */ struct FAccessToken {}; /** TAccessPtr * * TAccessPtr is used to determine whether an object has been destructed or not. * It is useful when an object cannot be wrapped in a TSharedPtr. A TAccessPtr * is functionally similar to a TWeakPtr, but it cannot pin the object. * In order to determine whether an object as accessible, the TAccessPtr * executes a TFunctions<> which returns object. If a nullptr is returned, * then the object is not accessible. * * Object pointers held within a TAccessPtr must only be accessed on * the thread where the objects gets destructed to avoid having the object * destructed while in use. * * If the TAccessPtr's underlying object is accessed when the pointer is invalid, * a fallback object will be returned. * * @tparam Type - The type of object to track. */ template class TAccessPtr { enum class EConstCast { Tag }; enum class EDerivedCopy { Tag }; public: using FTokenType = FAccessToken; using ObjectType = Type; static Type FallbackObject; /** Returns a pointer to the accessible object. If the object is * inaccessible, a nullptr is returned. */ Type* Get() const { #if METASOUND_FRONTEND_ACCESSPTR_DEBUG_INFO CachedObjectPtr = GetObject(); return CachedObjectPtr; #else return GetObject(); #endif } /** Returns an access pointer to a member of the wrapped object. * * @tparam AccessPtrType - The access pointer type to return. * @tparam FunctionType - A type which is callable accepts a reference to the wrapped object and returns a pointer to the member. * * @param InGetMember - A FunctionType accepts a reference to the wrapped object and returns a pointer to the member. */ template AccessPtrType GetMemberAccessPtr(FunctionType InGetMember) const { using MemberType = typename AccessPtrType::ObjectType; TFunction GetMemberFromObject = [GetObject=this->GetObject, GetMember=MoveTemp(InGetMember)]() -> MemberType* { if (Type* Object = GetObject()) { return GetMember(*Object); } return static_cast(nullptr); }; return AccessPtrType(GetMemberFromObject); } TAccessPtr() : GetObject([]() { return static_cast(nullptr); }) { #if METASOUND_FRONTEND_ACCESSPTR_DEBUG_INFO Get(); #endif } /** Creates a access pointer using an access token. */ TAccessPtr(TWeakPtr AccessToken, Type& InRef) { Type* RefPtr = &InRef; GetObject = [AccessToken=MoveTemp(AccessToken), RefPtr]() -> Type* { Type* Object = nullptr; if (AccessToken.IsValid()) { Object = RefPtr; } return Object; }; #if METASOUND_FRONTEND_ACCESSPTR_DEBUG_INFO Get(); #endif } /** Creates an access pointer from another using a const casts. */ template TAccessPtr(const TAccessPtr& InOther, EConstCast InTag) { GetObject = [GetOtherObject=InOther.GetObject]() -> Type* { return const_cast(GetOtherObject()); }; #if METASOUND_FRONTEND_ACCESSPTR_DEBUG_INFO Get(); #endif } /** Creates an access pointer from another using a static cast. */ template < typename OtherType UE_REQUIRES(std::is_convertible_v) > TAccessPtr(const TAccessPtr& InOther, EDerivedCopy InTag=EDerivedCopy::Tag) { GetObject = [GetOtherObject=InOther.GetObject]() -> Type* { return static_cast(GetOtherObject()); }; #if METASOUND_FRONTEND_ACCESSPTR_DEBUG_INFO Get(); #endif } TAccessPtr(const TAccessPtr& InOther) = default; TAccessPtr& operator=(const TAccessPtr& InOther) = default; TAccessPtr(TAccessPtr&& InOther) = default; TAccessPtr& operator=(TAccessPtr&& InOther) = default; protected: template friend RelatedAccessPtrType MakeAccessPtr(const FAccessPoint& InAccessPoint, RelatedType& InRef); template friend ToAccessPtrType ConstCastAccessPtr(const FromAccessPtrType& InAccessPtr); template friend class TAccessPtr; #if METASOUND_FRONTEND_ACCESSPTR_DEBUG_INFO mutable Type* CachedObjectPtr = nullptr; #endif TFunction GetObject; TAccessPtr(TFunction InGetObject) : GetObject(InGetObject) { #if METASOUND_FRONTEND_ACCESSPTR_DEBUG_INFO Get(); #endif } }; template Type TAccessPtr::FallbackObject = Type(); /** FAccessPoint acts as a lifecycle tracker for the TAccessPtrs it creates. * When this object is destructed, all associated TAccessPtrs will become invalid. */ class FAccessPoint { public: using FTokenType = FAccessToken; FAccessPoint() { Token = MakeShared(); } FAccessPoint(const FAccessPoint&) { // Do not copy token from other access point on copy. Token = MakeShared(); } // Do not copy token from other access point on assignment FAccessPoint& operator=(const FAccessPoint&) { return *this; } private: template friend AccessPtrType MakeAccessPtr(const FAccessPoint& InAccessPoint, Type& InRef); FAccessPoint(FAccessPoint&&) = delete; FAccessPoint& operator=(FAccessPoint&&) = delete; TSharedPtr Token; }; template AccessPtrType MakeAccessPtr(const FAccessPoint& InAccessPoint, Type& InRef) { return AccessPtrType(InAccessPoint.Token, InRef); } template ToAccessPtrType ConstCastAccessPtr(const FromAccessPtrType& InAccessPtr) { return ToAccessPtrType(InAccessPtr, ToAccessPtrType::EConstCast::Tag); } } }