// Copyright Epic Games, Inc. All Rights Reserved. #pragma once #include "CoreMinimal.h" #include "UObject/Class.h" #include "UObject/WeakObjectPtr.h" #include "UObject/Package.h" #include "Templates/Casts.h" #include "Templates/RemoveReference.h" class FStructOnScope { protected: TWeakObjectPtr ScriptStruct; uint8* SampleStructMemory; TWeakObjectPtr Package; /** Whether the struct memory is owned by this instance. */ bool OwnsMemory; virtual void Initialize() { if (const UStruct* ScriptStructPtr = ScriptStruct.Get()) { SampleStructMemory = (uint8*)FMemory::Malloc(ScriptStructPtr->GetStructureSize() ? ScriptStructPtr->GetStructureSize() : 1); ScriptStructPtr->InitializeStruct(SampleStructMemory); OwnsMemory = true; } } public: FStructOnScope() : SampleStructMemory(nullptr), OwnsMemory(false) { } FStructOnScope(const UStruct* InScriptStruct) : ScriptStruct(InScriptStruct), SampleStructMemory(nullptr), OwnsMemory(false) { Initialize(); } FStructOnScope(const UStruct* InScriptStruct, uint8* InData) : ScriptStruct(InScriptStruct), SampleStructMemory(InData), OwnsMemory(false) { } FStructOnScope(FStructOnScope&& InOther) { ScriptStruct = InOther.ScriptStruct; SampleStructMemory = InOther.SampleStructMemory; OwnsMemory = InOther.OwnsMemory; InOther.OwnsMemory = false; InOther.Reset(); } FStructOnScope& operator=(FStructOnScope&& InOther) { if (this != &InOther) { Reset(); ScriptStruct = InOther.ScriptStruct; SampleStructMemory = InOther.SampleStructMemory; OwnsMemory = InOther.OwnsMemory; InOther.OwnsMemory = false; InOther.Reset(); } return *this; } FStructOnScope(const FStructOnScope&) = delete; FStructOnScope& operator=(const FStructOnScope&) = delete; virtual bool OwnsStructMemory() const { return OwnsMemory; } virtual uint8* GetStructMemory() { return SampleStructMemory; } virtual const uint8* GetStructMemory() const { return SampleStructMemory; } virtual const UStruct* GetStruct() const { return ScriptStruct.Get(); } virtual UPackage* GetPackage() const { return Package.Get(); } virtual void SetPackage(UPackage* InPackage) { Package = InPackage; } virtual bool IsValid() const { return ScriptStruct.IsValid() && SampleStructMemory; } virtual void Destroy() { if (!OwnsMemory) { return; } if (const UStruct* ScriptStructPtr = ScriptStruct.Get()) { if (SampleStructMemory) { ScriptStructPtr->DestroyStruct(SampleStructMemory); } ScriptStruct = nullptr; } if (SampleStructMemory) { FMemory::Free(SampleStructMemory); SampleStructMemory = nullptr; } } virtual void Reset() { Destroy(); ScriptStruct = nullptr; SampleStructMemory = nullptr; OwnsMemory = false; } virtual ~FStructOnScope() { Destroy(); } /** Re-initializes the scope with a specified UStruct */ void Initialize(TWeakObjectPtr InScriptStruct) { Destroy(); ScriptStruct = InScriptStruct; Initialize(); } }; /** * Typed FStructOnScope that exposes type-safe access to the wrapped struct * @note The second template argument is there to restrict to type that are actually USTRUCT. * It will be replaced to a static_assert with a better compiler error with a "is ustruct" type trait */ template ::Get())> class TStructOnScope final: public FStructOnScope { public: TStructOnScope() = default; virtual ~TStructOnScope() = default; TStructOnScope(TStructOnScope&& InOther) = default; TStructOnScope& operator=(TStructOnScope&& InOther) = default; template ::Type, T>::IsDerived, void>::Type> explicit TStructOnScope(U&& InStruct) : FStructOnScope(TBaseStructure::Type>::Get()) { if (const UScriptStruct* ScriptStructPtr = ::Cast(ScriptStruct.Get())) { ScriptStructPtr->CopyScriptStruct(SampleStructMemory, &InStruct); } } template ::Type, T>::IsDerived, void>::Type> TStructOnScope& operator=(U&& InStruct) { Initialize(TBaseStructure::Type>::Get()); if (const UScriptStruct* ScriptStructPtr = ::Cast(ScriptStruct.Get())) { ScriptStructPtr->CopyScriptStruct(SampleStructMemory, &InStruct); } return *this; } /** * Initialize the TStructOnScope as a struct of type U which needs to derive from T * @params InArgs The arguments to pass to the constructor of type U */ template ::Type, T>::IsDerived, void>::Type, typename... TArgs> void InitializeAs(TArgs&&... InArgs) { Destroy(); if (UScriptStruct* ScriptStructPtr = TBaseStructure::Get()) { ScriptStruct = ScriptStructPtr; SampleStructMemory = (uint8*)FMemory::Malloc(ScriptStructPtr->GetStructureSize() ? ScriptStructPtr->GetStructureSize() : 1); new (SampleStructMemory) U(Forward(InArgs)...); OwnsMemory = true; } } /** * Initialize the TStructOnScope from a FStructOnScope containing data that derives from T * @params InOther The FStructOnScope to initialize from * @return True if the conversion was successful, false otherwise */ bool InitializeFrom(const FStructOnScope& InOther) { if (const UScriptStruct* ScriptStructPtr = ::Cast(InOther.GetStruct())) { if (ScriptStructPtr->IsChildOf(TBaseStructure::Get())) { Initialize(ScriptStructPtr); ScriptStructPtr->CopyScriptStruct(SampleStructMemory, InOther.GetStructMemory()); return true; } } else { Destroy(); return true; } return false; } /** * Initialize the TStructOnScope from a FStructOnScope containing data that derives from T * @params InOther The FStructOnScope to initialize from * @return True if the conversion was successful, false otherwise */ bool InitializeFrom(FStructOnScope&& InOther) { if (this == &InOther) { return true; } if (const UScriptStruct* ScriptStructPtr = ::Cast(InOther.GetStruct())) { if (ScriptStructPtr->IsChildOf(TBaseStructure::Get()) && InOther.OwnsStructMemory()) { *static_cast(this) = MoveTemp(InOther); return true; } } else { Destroy(); return true; } return false; } /** * Initialize the TStructOnScope from a FStructOnScope containing data that derives from T * @params InOther The FStructOnScope to initialize from (will assert if it contains an invalid type to store for T) */ void InitializeFromChecked(const FStructOnScope& InOther) { if (!InitializeFrom(InOther)) { UE_LOG(LogClass, Fatal, TEXT("Initialize of %s to %s failed"), *InOther.GetStruct()->GetName(), *TBaseStructure::Get()->GetName()); } } /** * Initialize the TStructOnScope from a FStructOnScope containing data that derives from T * @params InOther The FStructOnScope to initialize from (will assert if it contains an invalid type to store for T) */ void InitializeFromChecked(FStructOnScope&& InOther) { if (!InitializeFrom(MoveTemp(InOther))) { UE_LOG(LogClass, Fatal, TEXT("Initialize of %s failed"), *TBaseStructure::Get()->GetName()); } } T* Get() const { return reinterpret_cast(SampleStructMemory); } T* operator->() const { return Get(); } explicit operator bool() const { return IsValid(); } template U* Cast() { if (GetStruct()->IsChildOf(TBaseStructure::Get())) { return reinterpret_cast(SampleStructMemory); } return nullptr; } template const U* Cast() const { return const_cast(this)->Cast(); } template U* CastChecked() { U* Result = nullptr; if (!IsValid()) { UE_LOG(LogClass, Fatal, TEXT("Cast of nullptr to %s failed"), *TBaseStructure::Get()->GetName()); return Result; } Result = Cast(); if (!Result) { UE_LOG(LogClass, Fatal, TEXT("Cast of %s to %s failed"), *TBaseStructure::Get()->GetName(), *TBaseStructure::Get()->GetName()); } return Result; } template const U* CastChecked() const { return const_cast(this)->CastChecked(); } friend FArchive& operator<<(FArchive& Ar, TStructOnScope& InStruct) { if (Ar.IsLoading()) { FString StructPath; Ar << StructPath; if (!StructPath.IsEmpty()) { UScriptStruct* ScriptStructPtr = FindObject(nullptr, *StructPath, false); if (ScriptStructPtr == nullptr || !ScriptStructPtr->IsChildOf(TBaseStructure::Get())) { Ar.SetError(); return Ar; } InStruct.ScriptStruct = ScriptStructPtr; InStruct.Initialize(); ScriptStructPtr->SerializeItem(Ar, InStruct.SampleStructMemory, nullptr); } } // Saving else { FString StructPath; if (UScriptStruct* ScriptStructPtr = const_cast(::Cast(InStruct.ScriptStruct.Get()))) { StructPath = ScriptStructPtr->GetPathName(); Ar << StructPath; ScriptStructPtr->SerializeItem(Ar, InStruct.SampleStructMemory, nullptr); } else { Ar << StructPath; } } return Ar; } private: using FStructOnScope::Destroy; using FStructOnScope::GetPackage; using FStructOnScope::GetStructMemory; using FStructOnScope::Initialize; using FStructOnScope::OwnsStructMemory; using FStructOnScope::SetPackage; }; /** * Allocates a new struct of type U with the given arguments and returns it as a Typed StructOnScope of type T. * * @param Args The arguments to pass to the constructor of U. * * @return A TStructOnScope which holds a struct to a newly-constructed U with the specified Args. */ template FORCEINLINE TStructOnScope MakeStructOnScope(TArgs&&... Args) { TStructOnScope Struct; Struct.template InitializeAs(Forward(Args)...); return Struct; }