How to iterate over the UPROPERTIES of a class using TPropertyValueRange.
Warning: This feature is only available for Unreal Engine 5.0 and newer.
Intro
With the release of Unreal Engine 5, Epic Games has added a simpler and more powerful way to iterate through the properties of a UCLASS() or USTRUCT().
Why use it over TFieldIterator?
Contrary to TFieldIterator<>
, TPropertyValueRange<>
uses a range-based loop which allows you to filter by property type, and also retrieve the value directly.
This results in cleaner code and a streamlined process to read from/write to properties using Unreal’s reflection capabilities.
How does it work?
TPropertyValueRange<>
requires you to pass in three arguments:
- The type of
FProperty
you want to lookup. You can just useFProperty
to lookup all properties, or use something more specialised likeFStructProperty
. - The
UStruct*
you want to read the properties for (UClass*
forUObjects
,UScriptStruct*
for structs). - A pointer to an instance of the
UStruct*
you want to get the values of the properties for.
Let’s say I have the following struct:
USTRUCT()
struct FPlayerFlags
{
FPlayerFlags()
: bIsDeveloper(false)
, bIsAlive(true)
, DeathCount(0)
{}
UPROPERTY()
bool bIsDeveloper;
UPROPERTY()
bool bIsAlive;
UPROPERTY()
uint32 DeathCount;
}
In order to iterate through all properties using reflection, we can simply write the following range-based loop:
FPlayerFlags& Flags = MyPlayer->GetPlayerFlags();
for (const TPair<FProperty*, void*>& PropertyValuePair : TPropertyValueRange<FProperty>(FPlayerFlags::StaticStruct(), &Flags))
{
// The key of the PropertyValuePair contains information about the current property we're iterating at.
// The value is a void* to the property's value.
}
Now let’s say we want to set all bool properties to false, we could write the following code:
FPlayerFlags& Flags = MyPlayer->GetPlayerFlags();
for (const TPair<FProperty*, void*>& PropertyValuePair : TPropertyValueRange<FProperty>(FPlayerFlags::StaticStruct(), &Flags))
{
// Before casting the void* to your desired type, make sure the FProperty matches!
if (FBoolProperty* const BoolProperty = CastField<FBoolProperty>(PropertyValuePair.Key))
{
bool& bBoolPropertyValue = *static_cast<bool*>(PropertyValuePair.Value);
bBoolPropertyValue = false;
}
}
However, since TPropertyValueRange<>
allows us to only iterate through specific property types, we can simplify the code to this:
FPlayerFlags& Flags = MyPlayer->GetPlayerFlags();
for (const TPair<FBoolProperty*, void*>& PropertyValuePair : TPropertyValueRange<FBoolProperty>(FPlayerFlags::StaticStruct(), &Flags))
{
bool& bBoolPropertyValue = *static_cast<bool*>(PropertyValuePair.Value);
bBoolPropertyValue = false;
}
You can also use structured bindings to make your code even cleaner:
FPlayerFlags& Flags = MyPlayer->GetPlayerFlags();
for (const auto& [Property, Value] : TPropertyValueRange<FBoolProperty>(FPlayerFlags::StaticStruct(), &Flags))
{
bool& bBoolPropertyValue = *static_cast<bool*>(Value);
bBoolPropertyValue = false;
}
Conclusion
TPropertyValueRange<>
provides a clean and streamlined method to iterate through the UPROPERTY()
members of a UCLASS()
or USTRUCT()
.