Getting nested property values from properties?

Ive been using the previous posts here to compile an ultimate way of messing with FProperties and UProperties.

So far, a header like this works for wildcard parameters:

UFUNCTION(BlueprintCallable,Category="Logging",CustomThunk,meta=(CustomStructureParam="Data",DisplayName="Print This",Keywords="Print log Super this"))
	static void PrintThis(ACharacter* Target,UProperty* Data);

//Custom thunk must be used because we need the pointers to the values of the original data. You may add extra parameters to the function call, but it gets trickier.

DECLARE_FUNCTION(execPrintThis)
	{
		//Author credit to @dirkFist and pedro_clericuzzi on epicgames forums
		
		P_GET_OBJECT(ACharacter,Target);//This goes into the stack, or pointer, or property, or whatever. idk. but it grabs the value of the currently-selected parameters and assigns it to a variable in-scope. in this case, Target. This allows you to essentially SKIP to the next parameter whilst maintaining the integrity of the first. theres all kinds of P_GET_X, but since Target SHOULD be an object, we will use Object.
		
		//P_FINISH; = SKIP TO NEXT PARAMETER IF YOU DONT USE IT. IF YOU DO USE IT, WILL REMOVE THE PARAMETER FROM THE FUNCTION CALL. USE THIS AFTER YOURE FINISHED MESSING WITH THE PARAMETER IF YOU HAVENT USED P_GET_


//idk what the rest of this code does besides what the original author comments state. 

		// Steps into the stack, walking to the next property in it
		Stack.Step(Stack.Object,NULL);
		// Grab the last property found when we walked the stack
		// This does not contains the property value, only its type information
		FProperty* StructProperty = CastField<FProperty>(Stack.MostRecentProperty);
		// Grab the base address where the struct actually stores its data
		// This is where the property value is truly stored
		void* StructPtr = Stack.MostRecentPropertyAddress;
		// We need this to wrap up the stack
		P_FINISH;
		
		PrintThis_impl(StructProperty,StructPtr,Target);//Call the REAL function
	}

This is the CPP:

void USuperPrinterBPLibrary::PrintThis_impl(FProperty* Property, void* StructPtr,ACharacter* Target)
{
/*
FProperty = Property provided
StructPtr = a pointer to the address of Property. (this doesnt make sense since FProperty IS a pointer. Perhaps its pointing to the value of FProperty rather than the FProperty itself?
*/
	UE_LOG(AVSP,Log,TEXT("PROPERTY AUTHORED NAME: %s"),*Property->GetAuthoredName());
		//check if what was provided is a struct property, ie, a Struct. 
    	if(FStructProperty* StructProperty=CastField<FStructProperty>(Property))
    	{
    		//go into the struct properties and iterate over every property. 
    		for(TFieldIterator<FProperty> PropertyIt(StructProperty->Struct);PropertyIt;++PropertyIt)
    		{
    			UE_LOG(AVSP,Log,TEXT("VARIABLE NAME: %s"),*PropertyIt->GetAuthoredName());
    			//iterate over all struct properties, again? 
    			for(int32 ArrayIndex=0;ArrayIndex<PropertyIt->ArrayDim;ArrayIndex++)
    			{
    				//grabs the pointer where the property is stored
                    //why void? wtf is going on here? I think its assuming the properties has multiple properties within itself, hence the iteration loop for it. Why not make this a function? 
    				void* ValuePtr = PropertyIt->ContainerPtrToValuePtr<void>(StructPtr,ArrayIndex);
                              //parse the property into its property subtypes
    				ParseProperty(*PropertyIt,ValuePtr,Target);
    			}
    		}
    	}//this is my modification to the original post. The original code could not accommodate non-struct values. This allows you to pass in regular objects, like the player itself. Works like a charm, but i have no idea how it works and i loosely based this code from the code up above.  
else
    	{
    		UE_LOG(AVSP,Warning,TEXT("Param isnt a struct. Parsing property"));
    		//Parse property
    		ParseProperty(Property,StructPtr,Target);
    	}
}

finally, the parser:

void USuperPrinterBPLibrary::ParseProperty(FProperty* Property,void* ValuePtr,ACharacter* Target)
{
//why tf are we initializing variables here? ig it aint hurting anything
	float FloatValue;
	int32 IntValue;
	bool BoolValue;
	FString StringValue;
	FName NameValue;
	FText TextValue;
	//If property is Numeric
	if(FNumericProperty* NumericProperty=CastField<FNumericProperty>(Property))
	{
		if(NumericProperty->IsFloatingPoint())
		{
			FloatValue = NumericProperty->GetFloatingPointPropertyValue(ValuePtr);
			UE_LOG(AVSP, Log, TEXT("FLOAT VALUE: '%f'"), FloatValue);
		}else if(NumericProperty->IsInteger())
		{
			IntValue=NumericProperty->GetSignedIntPropertyValue(ValuePtr);
			UE_LOG(AVSP,Log,TEXT("INTERGER VALUE: '%i'"),IntValue);
		}
//If property is Boolean
	}else if(FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
	{
		BoolValue = BoolProperty->GetPropertyValue(ValuePtr);
		UE_LOG(AVSP,Log,TEXT("BOOL VALUE: %s"),((BoolValue)?TEXT("True"):TEXT("False")));
	}
//If property is an FName
	else if(FNameProperty* NameProperty = CastField<FNameProperty>(Property))
	{
		NameValue = NameProperty->GetPropertyValue(ValuePtr);
		UE_LOG(AVSP,Log,TEXT("NAME VALUE: %s"),*NameValue.ToString());
//If property is FString
	}else if(FStrProperty* StringProperty = CastField<FStrProperty>(Property))
	{
		StringValue = StringProperty->GetPropertyValue(ValuePtr);
		UE_LOG(AVSP, Log, TEXT("STRING VALUE:'%s'"), *StringValue);
//If property is Text
	}else if(FTextProperty* TextProperty = CastField<FTextProperty>(Property))
	{
		TextValue=TextProperty->GetPropertyValue(ValuePtr);
		UE_LOG(AVSP,Log,TEXT("TEXT VALUE: '%s'"),*TextValue.ToString());
//If property is an Array
	}else if(FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
	{
		FScriptArrayHelper Helper(ArrayProperty,ValuePtr);
		for(int32 i=0,n=Helper.Num();i<n;i++)
		{
			UE_LOG(AVSP,Log,TEXT("ARRAY: %i"),i);
			ParseProperty(ArrayProperty->Inner,Helper.GetRawPtr(i),Target);
		}
//my addition to the code; It can detect objects themselves. Atm, its hardcoded to cast to a Character and print out the Character's location. It needs to be able to get ALL properties of the character class, but i dont know how to get all the nested properties of a property this way. 
	}else if(FObjectProperty* ObjectProperty= CastField<FObjectProperty>(Property))
	{
		//Name of obj passed through
		UE_LOG(AVSP,Warning,TEXT("OBJECT NAME: %s"),*ObjectProperty->GetPropertyValue(ValuePtr).GetName());
		//Object type followed by address of object
		UE_LOG(AVSP,Warning,TEXT("OBJECT FULL NAME AFTER PROPERTY: %s"),*ObjectProperty->GetPropertyValue(ValuePtr).GetFullName());
		//Object type
		UE_LOG(AVSP,Warning,TEXT("OBJECT'S CLASS NAME: %s"),*ObjectProperty->GetPropertyValue(ValuePtr).GetClass()->GetFName().ToString());
		//Name of property
		UE_LOG(AVSP,Warning,TEXT("OBJECT AUTHORED NAME: %s"),*ObjectProperty->GetAuthoredName());
		//ObjectProperty followed by the property's static location/origin
		UE_LOG(AVSP,Warning,TEXT("OBJECT FULL NAME: '%s'"),*ObjectProperty->GetFullName());
		
		if(AActor* Actor = Cast<AActor>(ObjectProperty->GetPropertyValue(ValuePtr)))
		{
			UE_LOG(AVSP,Warning,TEXT("ACTOR LOCATION: %s"),*Actor->GetActorLocation().ToString());
		}
	}
	else if(Property)
	{
		PrintThis_impl(Property,ValuePtr,Target);
	}
}

This code 100% works but its too limited in design. I would really appreciate it if someone who is an expert with Properties shines a light here. The two problems I face are; how do you call a function that uses UProperty when UProperties are deprecated? How do you convert a FProperty to UProperty to call this function within CPP? And, how do you get nested properties from a property?

UProperty* Data is a dummy type here, the CustomStructureParam turns this parameter into a wildcard. You could replace UProperty* with int32 and it would work the same.

If you want to call it from C++, call PrintThis_impl directly and pass the FProperty and a pointer to the property value, for example :

// ACharacter* Char
FProperty* Prop = Char->GetClass()->FindPropertyByName("CrouchedEyeHeight");
void* Value = &Char->CrouchedEyeHeight;
PrintThis_Impl(Prop, Value, Char);

Note : you don’t seem to ever use the ACharacter* Target variable. What is it supposed to do?

You can iterate the properties of an object with something like this :

// UObject* Obj
for (TFieldIterator<FProperty> It(Obj->GetClass()); It; ++It)
{
    FProperty* InnerProp = *It;
    void* InnerValuePtr = InnerProp->ContainerPtrToValuePtr<void>(Obj);
    // iterate recursively
    ParseProperty(InnerProp, InnerValuePtr);
}

Similar thing for nested structs :

// FStructProperty* StructProp;
// void* StructPtr;
for (TFieldIterator<FProperty> It(StructProp->Struct); It; ++It)
{
    FProperty* InnerProp = *It;
    void* InnerValuePtr = InnerProp->ContainerPtrToValuePtr<void>(StructPtr);
    // iterate recursively
    ParseProperty(InnerProp, InnerValuePtr);
}

This is excellent! Target is just the object calling the function, and hasn’t been implemented yet. The nested structs and properties within objects works, but I was trying to get a way of getting properties from other properties. If I parse the FProperty back into FObjectProperty, is there a way to recursively get FProperties from the FObjectProperty?

Yes, you can get the ObjectProperty’s value (which would be an UObject), and iterate that object’s properties like I described above.

In your current ParseProperty method it would be something like :

void USuperPrinterBPLibrary::ParseProperty(FProperty* Property,void* ValuePtr,ACharacter* Target)
{
    //...
    else if(FObjectProperty* ObjectProperty = CastField<FObjectProperty>(Property))
    {
        // in UE5 the value is of type TObjectPtr<UObject>
        // before UE5 it would probably be UObject*

        TObjectPtr<UObject> Obj = ObjectProperty->GetPropertyValue(ValuePtr);
        if (Object)
        {
            for (TFieldIterator<FProperty> It(Obj->GetClass()); It; ++It)
            {
                FProperty* InnerProp = *It;
                void* InnerValuePtr = InnerProp->ContainerPtrToValuePtr<void>(Obj);
                ParseProperty(InnerProp, InnerValuePtr, Target);
            }
        }
    }
}

Be careful though, this will easily lead to infinite loops if you have circular object references (eg. ObjectA has a property referencing ObjectB, ObjectB has a property referencing ObjectA).

Amazing work! Once I get back to my computer, I’ll try to implement this and see if it works. (there might be an error with your code, seeing the if(Object) should probably be if(Obj). I’ll make that swap for you and see how it compiles)
Thank you so much for the help!

works wonderfully. my next question is, how do you pass the object itself through the function? like, the caller of the function, this? Ive noticed FProperty& Prop = this->GetClass()->PropertyLink; dont exactly work to getting the property of the current class