Hi,
You can check \Engine\Source\Developer\StructUtilsTestSuite\Private\PropertyBagTest.cpp for several test. I asked AI to add one showing how to iterate and print value of those properties. I ran it in the Editor successfully, here the test you can add to the file (AI inserted it on line 697):
struct FTest_IterateAndPrintValues : FAITestBase
{
virtual bool InstantTest() override
{
static const FName BoolName(TEXT("Bool"));
static const FName Int32Name(TEXT("Int32"));
static const FName FloatName(TEXT("Float"));
static const FName NameName(TEXT("Name"));
static const FName StringName(TEXT("String"));
static const FName EnumName(TEXT("Enum"));
static const FName StructName(TEXT("Struct"));
static const FName Int32ArrayName(TEXT("Int32Array"));
static const FName FloatSetName(TEXT("FloatSet"));
FInstancedPropertyBag Bag;
Bag.AddProperties({
{ BoolName, EPropertyBagPropertyType::Bool },
{ Int32Name, EPropertyBagPropertyType::Int32 },
{ FloatName, EPropertyBagPropertyType::Float },
{ NameName, EPropertyBagPropertyType::Name },
{ StringName, EPropertyBagPropertyType::String },
{ EnumName, EPropertyBagPropertyType::Enum, StaticEnum<EPropertyBagTest1>() },
{ StructName, EPropertyBagPropertyType::Struct, FTestStructHashable1::StaticStruct() },
{ Int32ArrayName, EPropertyBagContainerType::Array, EPropertyBagPropertyType::Int32 },
{ FloatSetName, EPropertyBagContainerType::Set, EPropertyBagPropertyType::Float },
});
AITEST_EQUAL(TEXT("Set bool should succeed"), Bag.SetValueBool(BoolName, true), EPropertyBagResult::Success);
AITEST_EQUAL(TEXT("Set int32 should succeed"), Bag.SetValueInt32(Int32Name, 42), EPropertyBagResult::Success);
AITEST_EQUAL(TEXT("Set float should succeed"), Bag.SetValueFloat(FloatName, 3.14f), EPropertyBagResult::Success);
AITEST_EQUAL(TEXT("Set name should succeed"), Bag.SetValueName(NameName, FName(TEXT("Hello"))), EPropertyBagResult::Success);
AITEST_EQUAL(TEXT("Set string should succeed"), Bag.SetValueString(StringName, TEXT("World")), EPropertyBagResult::Success);
AITEST_EQUAL(TEXT("Set enum should succeed"), Bag.SetValueEnum(EnumName, EPropertyBagTest1::Bar), EPropertyBagResult::Success);
FTestStructHashable1 StructValue;
StructValue.Float = 7.0f;
AITEST_EQUAL(TEXT("Set struct should succeed"), Bag.SetValueStruct(StructName, FConstStructView::Make(StructValue)), EPropertyBagResult::Success);
if (TValueOrError<FPropertyBagArrayRef, EPropertyBagResult> ArrayRes = Bag.GetMutableArrayRef(Int32ArrayName); ArrayRes.IsValid())
{
FPropertyBagArrayRef Array = ArrayRes.GetValue();
for (const int32 Value : { 10, 20, 30 })
{
const int32 Index = Array.AddValue();
Array.SetValueInt32(Index, Value);
}
}
if (TValueOrError<FPropertyBagSetRef, EPropertyBagResult> SetRes = Bag.GetMutableSetRef(FloatSetName); SetRes.IsValid())
{
FPropertyBagSetRef Set = SetRes.GetValue();
Set.AddValueFloat(1.5f);
Set.AddValueFloat(2.5f);
Set.AddValueFloat(3.5f);
}
const UPropertyBag* BagStruct = Bag.GetPropertyBagStruct();
AITEST_TRUE(TEXT("Bag struct should be valid"), BagStruct != nullptr);
int32 NumPropsSeen = 0;
for (const FPropertyBagPropertyDesc& Desc : BagStruct->GetPropertyDescs())
{
++NumPropsSeen;
const EPropertyBagContainerType ContainerType = Desc.ContainerTypes.GetFirstContainerType();
if (ContainerType == EPropertyBagContainerType::Array)
{
const TValueOrError<const FPropertyBagArrayRef, EPropertyBagResult> ArrayRes = Bag.GetArrayRef(Desc);
AITEST_TRUE(TEXT("Array ref should be valid"), ArrayRes.IsValid());
const FPropertyBagArrayRef& Array = ArrayRes.GetValue();
UE_LOG(LogTemp, Display, TEXT("Array %s [%d elem(s)]"), *Desc.Name.ToString(), Array.Num());
for (int32 Index = 0; Index < Array.Num(); ++Index)
{
if (Desc.ValueType == EPropertyBagPropertyType::Int32)
{
UE_LOG(LogTemp, Display, TEXT(" [%d] = %d"), Index, Array.GetValueInt32(Index).GetValue());
}
}
}
else if (ContainerType == EPropertyBagContainerType::Set)
{
// FPropertyBagSetRef does not expose element iteration in 5.7, so drop down to FScriptSetHelper.
const FSetProperty* SetProperty = CastField<FSetProperty>(Desc.CachedProperty);
AITEST_TRUE(TEXT("Set property should be valid"), SetProperty != nullptr);
const void* SetAddress = SetProperty->ContainerPtrToValuePtr<void>(Bag.GetValue().GetMemory());
FScriptSetHelper SetHelper(SetProperty, SetAddress);
UE_LOG(LogTemp, Display, TEXT("Set %s [%d elem(s)]"), *Desc.Name.ToString(), SetHelper.Num());
for (int32 Index = 0; Index < SetHelper.GetMaxIndex(); ++Index)
{
if (!SetHelper.IsValidIndex(Index))
{
continue;
}
const void* ElementPtr = SetHelper.GetElementPtr(Index);
if (Desc.ValueType == EPropertyBagPropertyType::Float)
{
UE_LOG(LogTemp, Display, TEXT(" %f"), *static_cast<const float*>(ElementPtr));
}
}
}
else
{
switch (Desc.ValueType)
{
case EPropertyBagPropertyType::Bool:
UE_LOG(LogTemp, Display, TEXT("Bool %s = %s"),
*Desc.Name.ToString(), Bag.GetValueBool(Desc).GetValue() ? TEXT("true") : TEXT("false"));
break;
case EPropertyBagPropertyType::Int32:
UE_LOG(LogTemp, Display, TEXT("Int32 %s = %d"),
*Desc.Name.ToString(), Bag.GetValueInt32(Desc).GetValue());
break;
case EPropertyBagPropertyType::Float:
UE_LOG(LogTemp, Display, TEXT("Float %s = %f"),
*Desc.Name.ToString(), Bag.GetValueFloat(Desc).GetValue());
break;
case EPropertyBagPropertyType::Name:
UE_LOG(LogTemp, Display, TEXT("Name %s = %s"),
*Desc.Name.ToString(), *Bag.GetValueName(Desc).GetValue().ToString());
break;
case EPropertyBagPropertyType::String:
UE_LOG(LogTemp, Display, TEXT("String %s = %s"),
*Desc.Name.ToString(), *Bag.GetValueString(Desc).GetValue());
break;
case EPropertyBagPropertyType::Enum:
{
const UEnum* EnumType = Cast<const UEnum>(Desc.ValueTypeObject);
const TValueOrError<uint8, EPropertyBagResult> EnumRes = Bag.GetValueEnum(Desc, EnumType);
UE_LOG(LogTemp, Display, TEXT("Enum %s = %s (%d)"),
*Desc.Name.ToString(),
EnumType ? *EnumType->GetNameStringByValue(EnumRes.GetValue()) : TEXT("<unknown>"),
EnumRes.GetValue());
}
break;
case EPropertyBagPropertyType::Struct:
{
const TValueOrError<FStructView, EPropertyBagResult> StructRes = Bag.GetValueStruct(Desc);
if (StructRes.IsValid())
{
if (const FTestStructHashable1* TypedPtr = StructRes.GetValue().GetPtr<const FTestStructHashable1>())
{
UE_LOG(LogTemp, Display, TEXT("Struct %s = (FTestStructHashable1 Float=%f)"),
*Desc.Name.ToString(), TypedPtr->Float);
}
}
}
break;
default:
UE_LOG(LogTemp, Display, TEXT("Other %s = <type %d not printed>"),
*Desc.Name.ToString(), static_cast<int32>(Desc.ValueType));
break;
}
}
}
AITEST_EQUAL(TEXT("Iteration should visit every property"), NumPropsSeen, 9);
return true;
}
};
IMPLEMENT_AI_INSTANT_TEST(FTest_IterateAndPrintValues, "System.StructUtils.PropertyBag.IterateAndPrintValues");
Regards,
Patrick