Possible to auto-compare ustructs without equality operator?

I’m guessing the answer is no, but I thought it was worth throwing the question out there.

I’ve got a number of UStructs like this and I’d rather not write out the equality operator for each one if there’s a simple way to do this. All of the relevant members are UProperties.

USTRUCT( BlueprintType )
struct FKani_InventoryItemWidgetInfo
{
	GENERATED_USTRUCT_BODY()

public:

	FKani_InventoryItemWidgetInfo() {}
	FKani_InventoryItemWidgetInfo( UObject* worldContextObject, TSubclassOf<AKani_Equipment> equipmentClass );

	UPROPERTY( BlueprintReadWrite )
	FText Label = FText::GetEmpty();

	UPROPERTY( BlueprintReadWrite )
	UTexture2D* Icon = nullptr;

	// if it doesn't exist, don't even show it!
	UPROPERTY( BlueprintReadWrite )
	bool bExists = true;

	// empty == not bound at all
	UPROPERTY( BlueprintReadWrite )
	bool bIsEmpty = true;

	// selected == you're currently holding it
	UPROPERTY( BlueprintReadWrite )
	bool bIsSelected = false;

	// it's available if you've discovered it and you can switch to it (not out of ammo)
	UPROPERTY( BlueprintReadWrite )
	bool bIsAvailable = false;

	UPROPERTY( BlueprintReadWrite )
	bool bShowInputActionWidget = false;

	UPROPERTY( BlueprintReadWrite )
	FName BoundInputActionName = "";

	UPROPERTY( BlueprintReadWrite )
	bool bGamepadAssignmentIsValid = false;

	UPROPERTY( BlueprintReadWrite )
	bool bIsGamepadAssignmentHighlighted = false;

	bool operator==( const FKani_InventoryItemWidgetInfo& otherInventoryItemWidgetInfo )
	{
		return this->Label.EqualTo(						   otherInventoryItemWidgetInfo.Label )
			&& this->Icon								== otherInventoryItemWidgetInfo.Icon
			&& this->bExists							== otherInventoryItemWidgetInfo.bExists
			&& this->bIsEmpty							== otherInventoryItemWidgetInfo.bIsEmpty
			&& this->bIsSelected						== otherInventoryItemWidgetInfo.bIsSelected
			&& this->bIsAvailable						== otherInventoryItemWidgetInfo.bIsAvailable
			&& this->bShowInputActionWidget				== otherInventoryItemWidgetInfo.bShowInputActionWidget
			&& this->BoundInputActionName				== otherInventoryItemWidgetInfo.BoundInputActionName
			&& this->bGamepadAssignmentIsValid			== otherInventoryItemWidgetInfo.bGamepadAssignmentIsValid
			&& this->bIsGamepadAssignmentHighlighted	== otherInventoryItemWidgetInfo.bIsGamepadAssignmentHighlighted;
	}
};

Usage case:

		//
		// Send data to the widget

		if( m_previousQuickSwapWidgetInfo != quickSwapWidgetInfo )
		{
			keyboardQuickSwapWidget->UpdateQuickSwapWidgetInfo( quickSwapWidgetInfo );
			m_previousQuickSwapWidgetInfo = quickSwapWidgetInfo;
		}

It’s not. Default comparisons don’t come in until C++ 20 (and even then, you still need to define the default)
https://en.cppreference.com/w/cpp/language/default_comparisons

You can make it “easier” by using FMemory::Memcmp instead, but I prefer the more explicit method. Note that you don’t need this->, you can access members directly. You can also make the parameter names much shorter. I always use RHS for equality operators myself.

You should also make the override const too. Note that Unreal’s internal functionality will not use the == operator for comparisons unless you define the relevant TStructOpsTypeTraits template class for it too.

Good call on the const and this-> (I dropped that pretty quickly as I started to write a dozen of equality operators). Even with C++ 20, I don’t think that will help since presumably every type within a struct will need its own equality operator and not every UE4 type necessarily does (FText, for instance, does not) and I like to avoid editing the engine code.

I guess Memcmp could work but that just feels wrong somehow. It seems possible that two instances of a structure could be logically equivalent even if their bytes aren’t exactly equal - who knows what happens within the internal memory of a container object? I could imagine two TSets that look different because things were added in a different order, for instance.

I suppose I was hoping something clever existed that utilized reflection to auto-compare all of the UPROPERTIES.

In that case, they are two different sets, as iterating on them would have different results.

Hi!

You could just define a free == operator, no need to change engine code (see below). The =default solution in C++20 is by far the nicest so I would go with that if you can.

Your feeling is right definitely don’t use memcmp for comparing structs, it most likely won’t give you the correct result anyway.

If you can’t use C++20 I would do it this way:

inline bool operator==(const FText& lhs, const FText& rhs)
{
  return lhs.EqualTo(rhs);
}

inline bool operator==(const FKani_InventoryItemWidgetInfo& lhs, const FKani_InventoryItemWidgetInfo& rhs)
{
  return Tie(lhs.Label, lhs.Icon, lhs.bExists, lhs.bIsEmpty, ...)
      == Tie(rhs.Label, rhs.Icon, rhs.bExists, rhs.bIsEmpty, ...);
}

I find it a bit cleaner than comparing all members individually.

If someone comes across this old post, this is the solution:

template <typename T>
bool StructEquals(const T& First, const T& Second)
{
    return T::StaticStruct()->CompareScriptStruct(&First, &Second, 0);
}

CompareScriptStruct is used by blueprint system itself as a generic way of comparing USTRUCTs based on their UPROPERTYs. It is invoked when using an Array.Find or Array.Contains blueprint node for example.

Also see my post about a solution for the opposite problem: Using a custom C++ == operator in these blueprint nodes instead

1 Like