Custom Struct == Operator with Array.Find

Hi guys,

I have a little problem with a custom C++ Struct being used in an Array in Blueprints.
If I use Array.Find to find an item inside, the overloaded == operator is not called. But if I use my custom Compare Method, written to expose this function to blueprints, it works as expected.
Can someone point to my mistake here?

.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Engine/UserDefinedStruct.h"
#include "FSaveStruct.generated.h"

USTRUCT(BlueprintType)
struct FSaveStruct 
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FGuid ObjectUUID;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	FString UserUUID;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	int32 ObjectState;

	//Constructor
	FSaveStruct()
	{
		UserUUID = "";
		ObjectState = 0;
	}

	inline bool operator==(const FSaveStruct& other) const
	{
		GEngine->AddOnScreenDebugMessage(-1, 5, FColor::Red, "OPERATOR OVERLOADED!");
		return other.ObjectUUID.ToString() == ObjectUUID.ToString() && other.UserUUID == UserUUID;
	}
};

UCLASS()
class FANTASYSURVIVAL_API UFSaveStructLib : public UBlueprintFunctionLibrary 
{
	GENERATED_BODY()
public:
	UFUNCTION(BlueprintPure, meta = (DisplayName = "Equal FSaveStruct", CompactNodeTitle = "==", Keywords = "== equal"), Category = "Math")
		static bool EqualEqual_FSaveStructFSaveStruct(const FSaveStruct &arg1, const FSaveStruct &arg2);
};

.cpp:

#include "FSaveStruct.h"
    
    bool UFSaveStructLib::EqualEqual_FSaveStructFSaveStruct(const FSaveStruct &arg1, const FSaveStruct &arg2)
    {
    	return arg1 == arg2;
    }

Blueprint “Code”:

I hope someone knows whats wrong here. I need to override the == operator, because I don’t want to compare all properties, because the Object State should not be compared (as this is different 99% of times).

Can you post the portion that’s attached to target on the blueprint that doesn’t work?

It’s possible that the data hasn’t replicated yet. Have you put a breakpoint and checked the contents of the array?

Sorry, maybe it wasn’t clear. If I do an ForEach Loop over the contents of the array and compare them with the Equals Method, then everything works. So the content is already there, the find method just does not call the overloaded operator.

It’s just a replicated array in the Game State. So this should not be the problem to why the operator isn’t called, me thinks ;D

Have you tried using FORCEINLINE instead of inline? Everything looks okay to me except that - maybe somehow reflection is messing stuff up if your overidden operator isn’t [something something weird reflection system magic dragons].

If not that, then what was the output of your debug stuff? Are you seeing the message when the operator runs? (I assume not if you’ve concluded the operator never fires.)

Same problem here. I used workaround in the meantime, but i would rly want to know how to get this to work properly.

Will check this when I get back home. But I think I had the FORCEINLINE in already, but we will see ;-D

This doesn’t work for me either

++ this. I’ve been ripping my hair out trying to figure out why blueprints can’t find my struct in the TMap, and this seems related. Sigh.

Here is a similar post that explains the reason for this issue. Hope this helps.

This is a really old post but just in case anyone else comes across this issue. You need to implement GetTypeHash inside your struct.

With the above example you’d probably want something like this:

friend uint32 GetTypeHash(const FSaveStruct& other)
	{
		return GetTypeHash(other.ObjectUUID.ToString() + other.UserUUID);
	}

This works really well with this struct example because this function should return something that makes this struct unique to all others. If you don’t have something like a GUID inside your struct then I’d just mirror your ==operator overload.

1 Like

I know this is a very old post, but maybe someone is still interested in a solution for the original question. I recently come across this issue myself and found a solution.

USTRUCT(BlueprintType)
struct MY_API FMyStruct
{
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	int32 PropertyField;
	
	uint16 Field;
	
	bool operator==(const FMyStruct& Other) const;
	{
		return PropertyField == Other.PropertyField && Field == Field;
	}
};

/**
 * These traits basically tell the system which functions are actually implemented in the struct that can be
 * automatically called by Unreal. Each enum entry stands for at least one function or constructor
 * that is implemented by the struct with the correct signature
 * (e.g 'WithSerializer = true' -> 'bool Serialize(FArchive& Ar)').
 * See 'TCppStructOps' in 'Class.h' for a full list of traits and functions.
 */
template <>
struct TStructOpsTypeTraits<FMyStruct> : TStructOpsTypeTraitsBase2<FMyStruct>
{
	enum
	{
		// Uses the 'operator==' when comparing the struct when using reflection. 
		WithIdenticalViaEquality = true,
	};
};

If can confirm that the custom equality operator is indeed getting called by using an Array.Find node in blueprints.

Note that the default implemention seems to compare all UProperties of the struct. So this is only needed if you have other members of your struct that are not flagged with UPROPERTY or just want to compare a subset of members of your struct. See Class.cpp: UScriptStruct::CompareScriptStruct() for reference.

Is this still relevant on UE5?

Can you elaborate on your question? If there has been a change to the struct-trait-system then I’m not aware of it.

1 Like

That was what I wanted to know :slightly_smiling_face: Because I was not aware that blueprint does not use the == operator by default defined in c++ I now have to double check hundreds of structs :yawning_face: .

1 Like

So you may be interested in a solution to the opposite problem :wink:

This way you can make sure that both, C++ and Blueprints, use at least same code path when comparing structs that only have fields flagged with UPROPERTY.

2 Likes