// Copyright Epic Games, Inc. All Rights Reserved. /*============================================================================= FieldIterator.h: FField iterators. =============================================================================*/ #pragma once #include "CoreMinimal.h" #include "UObject/Field.h" #include "UObject/UObjectIterator.h" /** * Helper function for getting the inner fields of a field that works with both FFields and UFields */ template void GetInnerFieldsFromField(FieldType* Owner, TArray& OutFields) { check(false); } template <> inline void GetInnerFieldsFromField(FField* Owner, TArray& OutFields) { Owner->GetInnerFields(OutFields); } template <> inline void GetInnerFieldsFromField(UField* Owner, TArray& OutFields) { } // // For iterating through all fields in all structs including inner FProperties of top level FProperties. // template class TAllFieldsIterator { private: /** Iterator for iterating over all UStructs */ TObjectIterator StructIterator; /** * Iterator for iterating over all child UFields or FFields * Note that we're going to be iterating over all fields (BaseFieldClass is either FField or UField), not just the ones that match the template argument because we also want * to be able to iterate inner fields (for example FArrayProperty::Inner, FSetProperty::ElementProp etc) */ TFieldIterator FieldIterator; /** List containing the currently iterated field as well as all fields it owns and all fields the fields it owns own etc.. */ TArray CurrentFields; /** Currently iterated field index in CurrentFields array */ int32 CurrentFieldIndex = -1; public: TAllFieldsIterator(EObjectFlags AdditionalExclusionFlags = RF_ClassDefaultObject, EInternalObjectFlags InternalExclusionFlags = EInternalObjectFlags::None) : StructIterator(AdditionalExclusionFlags, /*bIncludeDerivedClasses =*/true, InternalExclusionFlags), FieldIterator(nullptr) { // Currently 3 would be enough (the current field + its inners which is 2 max for FMapProperty) but we keep one extra as slack // We never free this array memory inside of TAllFieldsIterator except when TAllFieldsIterator gets destroyed for performance reasons so it may only grow. // In the future we may want to support TArrays of TArrays/TMaps (nested containers) and in such case it may grow beyond 4 but that's ok CurrentFields.Reserve(4); InitFieldIterator(); } /** conversion to "bool" returning true if the iterator is valid. */ FORCEINLINE explicit operator bool() const { return (bool)FieldIterator || (bool)StructIterator; } /** inverse of the "bool" operator */ FORCEINLINE bool operator!() const { return !(bool)*this; } inline friend bool operator==(const TAllFieldsIterator& Lhs, const TAllFieldsIterator& Rhs) { return *Lhs.FieldIterator == *Rhs.FieldIterator && Lhs.CurrentFieldIndex == Rhs.CurrentFieldIndex; } inline friend bool operator!=(const TAllFieldsIterator& Lhs, const TAllFieldsIterator& Rhs) { return *Lhs.FieldIterator != *Rhs.FieldIterator || Lhs.CurrentFieldIndex != Rhs.CurrentFieldIndex; } inline void operator++() { IterateToNextField(); ConditionallyIterateToNextStruct(); } inline T* operator*() { if (CurrentFieldIndex >= 0) { return CastFieldChecked(CurrentFields[CurrentFieldIndex]); } return nullptr; } inline T* operator->() { if (CurrentFieldIndex >= 0) { return CastFieldChecked(CurrentFields[CurrentFieldIndex]); } return nullptr; } protected: /** Initializes CurrentFields array with the currently iterated field as well as the fields it owns */ inline void InitCurrentFields() { CurrentFieldIndex = -1; CurrentFields.Reset(); typename T::BaseFieldClass* CurrentField = *FieldIterator; CurrentFields.Add(CurrentField); GetInnerFieldsFromField(CurrentField, CurrentFields); } /** Advances to the next field of the specified template type */ inline void IterateToNextField() { while (FieldIterator) { for (++CurrentFieldIndex; CurrentFieldIndex < CurrentFields.Num(); ++CurrentFieldIndex) { if (CurrentFields[CurrentFieldIndex]->template IsA()) { break; } } if (CurrentFieldIndex == CurrentFields.Num()) { ++FieldIterator; if (FieldIterator) { InitCurrentFields(); } else { CurrentFieldIndex = -1; } } else { break; } } } /** Initializes the field iterator for the current struct */ inline void InitFieldIterator() { while (StructIterator) { FieldIterator.~TFieldIterator(); new (&FieldIterator) TFieldIterator(*StructIterator, EFieldIteratorFlags::ExcludeSuper, EFieldIteratorFlags::IncludeDeprecated, EFieldIteratorFlags::IncludeInterfaces); if (!FieldIterator) { // This struct has no fields, check the next one ++StructIterator; CurrentFieldIndex = -1; } else { InitCurrentFields(); IterateToNextField(); if (!FieldIterator) { // If the field iterator is invalid after IterateToNextField() call then no fields of the speficied template type were found ++StructIterator; } else { break; } } } } inline void ConditionallyIterateToNextStruct() { if (!FieldIterator) { // We finished iterating over all fields of the current struct so move to the next struct ++StructIterator; InitFieldIterator(); } } };