[Tutorial] How to accept wildcard structs in your UFUNCTIONs

Ok, started from scratch the following compiles and runs without error (on my machine)



#pragma once
// Example Blueprint function that receives any struct as input
#include "Kismet/BlueprintFunctionLibrary.h"
#include "StructBPLibrary.generated.h"

/* 
*	Function library class.
*	Each function in it is expected to be static and represents blueprint node that can be called in any blueprint.
*
*	When declaring function you can define metadata for the node. Key function specifiers will be BlueprintPure and BlueprintCallable.
*	BlueprintPure - means the function does not affect the owning object in any way and thus creates a node without Exec pins.
*	BlueprintCallable - makes a function which can be executed in Blueprints - Thus it has Exec pins.
*	DisplayName - full name of the node, shown when you mouse over the node and in the blueprint drop down menu.
*				Its lets you name the node using characters not allowed in C++ function names.
*	CompactNodeTitle - the word(s) that appear on the node.
*	Keywords -	the list of keywords that helps you to find node when you search for it using Blueprint drop-down menu. 
*				Good example is "Print String" node which you can find also by using keyword "log".
*	Category -	the category your node will be under in the Blueprint drop-down menu.
*
*	For more info on custom blueprint nodes visit documentation:
*	https://wiki.unrealengine.com/Custom_Blueprint_Node_Creation
*/
UCLASS()
class UStructBPLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_UCLASS_BODY()

	UFUNCTION(
		BlueprintCallable, 
		Category = "Example",
		CustomThunk,
		meta = (
			Keywords = "Struct sample test testing",
			CustomStructureParam = "AnyStruct"
	))
	static void ReceiveSomeStruct(UStruct* AnyStruct);

	DECLARE_FUNCTION(execReceiveSomeStruct)
	{
		// 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;

		// Iterate through the struct
		ReceiveSomeStruct_impl(StructProperty, StructPtr);
	}

	/*
	* Example function for iterating through all properties of a struct
	* @param StructProperty    The struct property reflection data
	* @param StructPtr        The pointer to the struct value
	*/
	static void ReceiveSomeStruct_impl(FProperty* Property, void* StructPtr)
	{
		UE_LOG(LogTemp, Warning, TEXT("%s"), *Property->GetAuthoredName());
		FStructProperty* StructProperty = CastField<FStructProperty>(Property);
		//check for null
		if (StructProperty)
		{
			// Walk the structs' properties
			for (TFieldIterator<FProperty> PropertyIt(StructProperty->Struct); PropertyIt; ++PropertyIt)
			{
				// This is the variable name if you need it
				UE_LOG(LogTemp, Warning, TEXT("%s"), *PropertyIt->GetAuthoredName());
				// Never assume ArrayDim is always 1
				for (int32 ArrayIndex = 0; ArrayIndex < PropertyIt->ArrayDim; ArrayIndex++)
				{
					// This grabs the pointer to where the property value is stored
					void* ValuePtr = PropertyIt->ContainerPtrToValuePtr<void>(StructPtr, ArrayIndex);

					// Parse this property
					ParseProperty(*PropertyIt, ValuePtr);
				}
			}
		}
	}

	/* Example function for parsing a single property
	* @param Property    the property reflection data
	* @param ValuePtr    the pointer to the property value
	*/
	static void ParseProperty(FProperty* Property, void* ValuePtr)
	{
		float FloatValue;
		int32 IntValue;
		bool BoolValue;
		FString StringValue;
		FName NameValue;
		FText TextValue;

		// Here's how to read integer and float properties
		if (FNumericProperty* NumericProperty = CastField<FNumericProperty>(Property))
		{
			if (NumericProperty->IsFloatingPoint())
			{
				FloatValue = NumericProperty->GetFloatingPointPropertyValue(ValuePtr);
				UE_LOG(LogTemp, Warning, TEXT("integer%d]"), FloatValue);
			}
			else if (NumericProperty->IsInteger())
			{
				IntValue = NumericProperty->GetSignedIntPropertyValue(ValuePtr);
				UE_LOG(LogTemp, Warning, TEXT("integer:'%i'"), IntValue);
			}
		}
		// How to read booleans
		else if (FBoolProperty* BoolProperty = CastField<FBoolProperty>(Property))
		{
			BoolValue = BoolProperty->GetPropertyValue(ValuePtr);
			if (BoolValue) 
			{
				UE_LOG(LogTemp, Warning, TEXT("Bool: True"));
			}
			else 
			{
				UE_LOG(LogTemp, Warning, TEXT("Bool: False"));
			}
		}

		// Reading names
		else if (FNameProperty* NameProperty = CastField<FNameProperty>(Property))
		{
			NameValue = NameProperty->GetPropertyValue(ValuePtr);
			UE_LOG(LogTemp, Warning, TEXT("Name:'%s'"), *NameValue.ToString());

		}

		// Reading strings
		else if (FStrProperty* StringProperty = CastField<FStrProperty>(Property))
		{
			StringValue = StringProperty->GetPropertyValue(ValuePtr);
			UE_LOG(LogTemp, Warning, TEXT("String:'%s'"), *StringValue);
		}

		// Reading texts
		else if (FTextProperty* TextProperty = CastField<FTextProperty>(Property))
		{
			TextValue = TextProperty->GetPropertyValue(ValuePtr);
			UE_LOG(LogTemp, Warning, TEXT("Text:'%s'"), *TextValue.ToString());
		}

		// Reading an array
		else if (FArrayProperty* ArrayProperty = CastField<FArrayProperty>(Property))
		{
			// We need the helper to get to the items of the array       
			FScriptArrayHelper Helper(ArrayProperty, ValuePtr);
			for (int32 i = 0, n = Helper.Num(); i < n; ++i)
			{
				UE_LOG(LogTemp, Warning, TEXT("Array:%i"), i);
				ParseProperty(ArrayProperty->Inner, Helper.GetRawPtr(i));
			}
		}

		// Reading a nested struct
		else if (Property)
		{
			ReceiveSomeStruct_impl(Property, ValuePtr);
		}
	}
};


1 Like