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);
}
}
};