Download

Get class of a UStructProperty from Reflection

Let’s say, some object has a UStructProperty. We get it like this:



auto lStructProperty = FindFieldChecked<UStructProperty>(lClass, *lFieldName);


We can get a reference to the raw structure like this:



void * lStructure = lStructProperty->ContainerPtrToValuePtr<void>(aObject);


After that there must be a way to get info about a structure in this pointer, that is the big question of this thread:



UStruct lStruct = ????


Here is the complete code I’m trying to get working:



/*
* Get final path to a property from a path like "SubPropertyAtLEvel1.SubPropertyAtLEvel2.FinalPropertyName"
*/
bool UStrangeUtilities_RTTI::GetFinalPropertyPath(UObject * & aObject, FName & aPropertyName)
{
    UClass* lClass = aObject->GetClass();
    //bool lInnerStructFound;
    bool lInnerClassFound;
    do
    {
        FString lObjectName;
        FString lFieldName;
        /*
        lInnerStructFound = aPropertyName.ToString().Split(".", &lObjectName, &lFieldName, ESearchCase::CaseSensitive);

        if (lInnerStructFound)
        {
            auto lStructProperty = FindFieldChecked<UStructProperty>(lClass, *lObjectName);

            if (lStructProperty != nullptr)
            {
                return false; //Error.
            };
            void * lStructure = lStructProperty->ContainerPtrToValuePtr<void>(aObject);

            // Find inner struct?
            UStruct lStruct = UStruct::StaticStruct();
            lClass = aObject->GetClass();
            aPropertyName = *lFieldName;
        }
        */

        lInnerClassFound = aPropertyName.ToString().Split(".", &lObjectName, &lFieldName, ESearchCase::CaseSensitive);

        if (lInnerClassFound)
        {
            auto lObjectProperty = FindFieldChecked<UObjectProperty>(lClass, *lObjectName);

            if (lObjectProperty != nullptr)
            {
                return false; // Error.
            };

            aObject = lObjectProperty->GetPropertyValue_InContainer(aObject, 0);
            lClass = aObject->GetClass();
            aPropertyName = *lFieldName;
        }

    } while (lInnerClassFound);

    return true;
}


I’m testing it on this set of classes:



/**
* Dummy test class 1.
*/
UCLASS(BlueprintType)
class UAstraUtilities_UnitTests_TestClass1: public UObject
{
    GENERATED_BODY()
public:

    /**
    * Dummy property 1.
    */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Astra")
    FString StringProperty;

    /**
    * Dummy property 2.
    */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Astra")
    float FloatProperty;
};


/**
* Dummy test structure 2.
*/
USTRUCT(BlueprintType)
struct FAstraUtilities_UnitTests_TestStruct2
{
    GENERATED_BODY()
public:
    /**
    * Dummy property 1.
    */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Astra")
    UAstraUtilities_UnitTests_TestClass1 * Property1;



    /**
    * Dummy constructor.
    */
    FAstraUtilities_UnitTests_TestStruct2()
        :
        Property1(NewObject<UAstraUtilities_UnitTests_TestClass1>())
    {
        // Nothing here.
    }
};


/**
* Dummy test class 3.
*/
UCLASS(BlueprintType)
class UAstraUtilities_UnitTests_TestClass3 : public UObject
{
    GENERATED_BODY()
public:
    /**
    * Dummy property 1.
    */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Astra")
    UAstraUtilities_UnitTests_TestClass1 * ObjectProperty1 = NewObject<UAstraUtilities_UnitTests_TestClass1>();

    /**
    * Dummy property 2.
    */
    UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Astra")
    FAstraUtilities_UnitTests_TestStruct2 StructProperty2;
};





for ( TFieldIterator<UProperty> PIT (MyObject->GetClass(), EFieldIteratorFlags::IncludeSuper); PIT; ++PIT ) {

    UProperty* Property = *PIT;

    const bool IsStruct = Property->IsA( UStructProperty::StaticClass() );

    if ( IsStruct && CastChecked<UStructProperty>(Property)->Struct == TBaseStructure<FVector>::Get() )
    {
        FVector NewVector;
          NewVector.X = 30.f;
          NewVector.Y = 60.f;
          NewVector.Z = 90.f;

        auto StructPtr = Property->ContainerPtrToValuePtr<FVector>(Container);

        if (StructPtr)
        {
            (*StructPtr) = NewVector;
        }
    }

    if ( IsStruct && CastChecked<UStructProperty>(Property)->Struct == TBaseStructure<FTransform>::Get() )
    {
        //...
    }

}


You are suggesting that we somehow know that property with the given name is a FVector or a FTransform. But what if we don’t? Epic has more general solution to this problem in its Editor. I even tried looking through that code, but was too complicated.

The type info will always be stored somewhere, usually as a pin category struct which is used within that property type selector in editors.
All they do is store schema strings, UK2Schema::PinType.
It’s just a string, “exec”, “float”, etc…
Somewhere else down the road the engine is going to cast based on the string stored in the pin, somewhat like in code above.