Is there any obvious reason why structs break my arrays?

I’m trying to set up what I thought would be simple: every time a character from my game spawns in the map, their character data, encapsulated in some struct FCharacterSheet, gets moved from a list of inactive NPCs to active ones, and vice versa. I was under the impression you could store structs in arrays, but I get an error every time I try to run .Add(), .Contains(), or .Remove() with my struct as the argument. Am I doing this wrong, or is it simply not possible to store structs the way I’m proposing?


 USTRUCT()
 struct FCharacterSheet{
     GENERATED_USTRUCT_BODY()
 
     UPROPERTY(BlueprintReadOnly)
     FString name;
   
     UPROPERTY(BlueprintReadOnly)
     AActor* myActor;
     UPROPERTY(BlueprintReadOnly)
         float health;
     UPROPERTY(BlueprintReadOnly)
         float stamina;
 };

     UPROPERTY(VisibleAnywhere)
     TArray<FCharacterSheet> InactivePool; //Characters not currently spawned in the world
 
     UPROPERTY(VisibleAnywhere)
     TArray<FCharacterSheet> ActivePool; //Characters not currently spawned in the world 

 void UPopulationManager::MakeInactive(FCharacterSheet character){
     if (!InactivePool.Contains(character)){ 
         InactivePool.Add(character);//Adds character to inactive pool
         ActivePool.Remove(character); //Removes character from active pool
     }
 }

 void UPopulationManager::MakeActive(FCharacterSheet character){
     if (!ActivePool.Contains(character)){ 
         ActivePool.Add(character);//Adds character to active pool
         InactivePool.Remove(character); //Removes character from inactive pool
     }
 }

I’m using TArrays of structs without any problems. Are you declaring the USTRUCT inside a UCLASS? Perhaps moving the struct definition outside of the class helps.

What is the error by the way?

That’s weird, I’m not… the declaration is in my .h just under the #includes, and before the UCLASS declaration, and the functions I quoted are from the .cpp .

The error is really hard to decipher, this is the entire thing:

Info c:\users\username\documents\unreal projects\projectname\source\ projectname \PopulationManager.h(37) : see reference to class template instantiation ‘TArray<FCharacterSheet,FDefaultAllocator>’ being compiled
Info C:\Program Files (x86)\Epic Games\4.8\Engine\Source\Runtime\Core\Public\Containers\Array.h(1965) : error C2678: binary ‘==’ : no operator found which takes a left-hand operand of type ‘FCharacterSheet’ (or there is no acceptable conversion)
Info C:\Program Files (x86)\Epic Games\4.8\Engine\Source\Runtime\Engine\Classes\Sound/DialogueWave.h(36): could be ‘bool operator ==(const FDialogueContextMapping &,const FDialogueContextMapping &)’
Info C:\Program Files (x86)\Epic Games\4.8\Engine\Source\Runtime\Engine\Classes\Sound/DialogueTypes.h(56): or ‘bool operator ==(const FDialogueContext &,const FDialogueContext &)’
Info C:\Program Files (x86)\Epic Games\4.8\Engine\Source\Runtime\Engine\Classes\Engine/StaticMesh.h(209): or ‘bool operator ==(const FMeshSectionInfo &,const FMeshSectionInfo &)’
Info C:\Program Files (x86)\Epic Games\4.8\Engine\Source\Runtime\Engine\Classes\Engine/SkeletalMesh.h(514): or ‘bool operator ==(const UMaterialInterface &,const FSkeletalMaterial &)’
Info C:\Program Files (x86)\Epic Games\4.8\Engine\Source\Runtime\Engine\Classes\Engine/SkeletalMesh.h(513): or ‘bool operator ==(const FSkeletalMaterial &,const UMaterialInterface &)’
Info C:\Program Files (x86)\Epic Games\4.8\Engine\Source\Runtime\Engine\Classes\Engine/SkeletalMesh.h(512): or ‘bool operator ==(const FSkeletalMaterial &,const FSkeletalMaterial &)’
Info c:\program files (x86)\epic games\4.8\engine\source\runtime\rhi\public\RHIResources.h(179): or ‘bool operator ==(const FRHIUniformBufferLayout &,const FRHIUniformBufferLayout &)’
Info C:\Program Files (x86)\Windows Kits\8.1\include\shared\guiddef.h(192): or ‘bool operator ==(const GUID &,const GUID &)’
Info while trying to match the argument list ‘(FCharacterSheet, const FCharacterSheet)’
Info C:\Program Files (x86)\Epic Games\4.8\Engine\Source\Runtime\Core\Public\Containers\Array.h(1961) : while compiling class template member function ‘int32 TArray<FCharacterSheet,FDefaultAllocator>::Remove(const FCharacterSheet &)’
Info C:\Filepath\Source\ProjectName\PopulationManager.cpp(35) : see reference to function template instantiation ‘int32 TArray<FCharacterSheet,FDefaultAllocator>::Remove(const FCharacterSheet &)’ being compiled

These are the two lines from my project it references:

PopulationManager.h (37):


TArray<FCharacterSheet> InactivePool; //Characters not currently spawned in the world

PopulationManager.cpp(35):


InactivePool.Remove(character); //Removes character from active pool

TArray.Contains() needs to know how to compare two of your structs for equality. So you need to define a function for that, for example:



bool operator==(const FMyStruct& lhs, const FMyStruct& rhs);


Huh, I’ve never run into this before… what exactly does the “bool operator ==” section accomplish, does this need to be specific to the data structure of the struct?

Maybe previously you never called TArray.Contains with a custom struct before? The function “bool operator==(const FMyStruct& lhs, const FMyStruct& rhs);” defines when two instances of FMyStruct are equal It needs a function body too. Since TArray.Contains uses == internally, it must exist. Here is how I defined it for my struct FVector2i, which stores 2D integer coordinates:

Vector2i.h:



USTRUCT(BlueprintType)
struct FVector2i
{
	GENERATED_USTRUCT_BODY()

	FVector2i() : X(0), Y(0) {}
	FVector2i(const int32 X, const int32 Y) : X(X), Y(Y) {}

	UPROPERTY(BlueprintReadWrite)
	int32 X;
	UPROPERTY(BlueprintReadWrite)
	int32 Y;
};

bool operator==(const FVector2i& lhs, const FVector2i& rhs);


Vector2i.cpp:



bool operator==(const FVector2i& lhs, const FVector2i& rhs)
{
	return lhs.X == rhs.X && lhs.Y == rhs.Y;
}


Oh, that is cool. There is no need to compare every value if you don’t need to, right? So if I have a data-heavy object I could theoretically give each one a unique int32 GUID, and test for equality across the whole struct by comparing that one value in my function?

Does this look right to you? UE is crashing when I try to compile it. These are both from a component UCharacterData, which is where FCharacterSheet is declared and defined:

CharacterData.h:


#pragma once
#include "CharacterData.generated.h"

USTRUCT()
struct FCharacterSheet{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(BlueprintReadOnly)
	FString name;
	//	int age;
	UPROPERTY(BlueprintReadOnly)
	AActor* myActor;
	UPROPERTY(BlueprintReadOnly)
		float health;
	UPROPERTY(BlueprintReadOnly)
		float stamina;
	UPROPERTY(BlueprintReadOnly)
		float maxHealth;
	UPROPERTY(BlueprintReadOnly)
		float maxStamina;

	float height;
	float weight;
};

bool operator==(const FCharacterSheet& lhs, const FCharacterSheet& rhs);

UCLASS()
class PROJECTNAME_API UCharacterData : public UMorpheme
{
	GENERATED_BODY()
public:
	UCharacterData();


	// a bunch of unrelated decs follow
}

CharacterData.cpp:



#include "projectname.h"
#include "CharacterData.h"


bool operator==(const FCharacterSheet& lhs, const FCharacterSheet& rhs){

	return lhs.name == rhs.name &&
		lhs.height == rhs.height;
}

UCharacterData::UCharacterData(){
RandomizeStatistics();

}

//a bunch of other functions follow

Yep! And I agree that is cool.

I don’t see any issues with the code. What do you mean by UE crashes when you compile it?

When I compile, it runs for a while, then UE consistently crashes. I pasted the crashlog before, but heck if I can see what the problem is from its contents:

Edit: I think it must’ve been a weird residual error from the original problem: I “fixed” it by commenting everything in the class out, compiling successfully, then uncommenting and recompiling. Nothing changed, but it works now :slight_smile:

Unknown exception - code 00000001 (first/second chance not available)

"Assertion failed: SubobjectInits[Index].Subobject != Subobject [File:d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\publ

UE4Editor_Core!FDebug::AssertFailed() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\core\private\misc\outputdevice.cpp:355]
UE4Editor_CoreUObject!FObjectInitializer::FSubobjectsToInit::Add() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\public\uobject\uobjectglobals.h:944]
UE4Editor_CoreUObject!FObjectInitializer::CreateDefaultSubobject() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\uobjectglobals.cpp:2982]
UE4Editor_CoreUObject!UObject::CreateDefaultSubobject() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\obj.cpp:88]
UE4Editor_ProjectName_2953!UPopulationManager::UPopulationManager() [c:\projectpath \populationmanager.cpp:16]
UE4Editor_CoreUObject!StaticConstructObject_Internal() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\uobjectglobals.cpp:2539]
UE4Editor_CoreUObject!FDuplicateDataWriter::GetDuplicatedObject() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\serialization\duplicatedatawriter.cpp:138]
UE4Editor_CoreUObject!FDuplicateDataWriter::operator<<() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\serialization\duplicatedatawriter.cpp:55]
UE4Editor_CoreUObject!UObjectProperty::SerializeItem() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\propertyobject.cpp:32]
UE4Editor_CoreUObject!FPropertyTag::SerializeTaggedProperty() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\public\uobject\propertytag.h:145]
UE4Editor_CoreUObject!UStruct::SerializeTaggedProperties() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\class.cpp:1313]
UE4Editor_CoreUObject!UObject::SerializeScriptProperties() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\obj.cpp:985]
UE4Editor_CoreUObject!UObject::Serialize() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\obj.cpp:927]
UE4Editor_CoreUObject!StaticDuplicateObjectEx() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\uobjectglobals.cpp:1603]
UE4Editor_CoreUObject!StaticDuplicateObject() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\uobjectglobals.cpp:1517]
UE4Editor_CoreUObject!UClassGenerateCDODuplicatesForHotReload() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\uobjectbase.cpp:701]
UE4Editor_CoreUObject!ProcessNewlyLoadedUObjects() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\coreuobject\private\uobject\uobjectbase.cpp:813]
UE4Editor_CoreUObject!TBaseStaticDelegateInstance<void cdecl(void)>::ExecuteIfSafe() [d:\buildfarm\buildmachine++depot+ue4-releases+4.8\engine\source\runtime\core\public\delegates\delegateinstancesimpl_variadics.inl:921]
UE4Editor_Core!TBaseMulticastDelegate<void>::Broadcast() [d:\buildfarm\buildmachine
++depot+ue4-releases+4.8\engine\source\runtime\core\public\delegates\delegatesignatureimpl_variadics.inl:1031]
UE4Editor_Core!FModuleManager::LoadModuleWithFailureReason() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\core\private\modules\modulemanager.cpp:431]
UE4Editor_Core!FModuleManager::LoadModule() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\runtime\core\private\modules\modulemanager.cpp:303]
UE4Editor_HotReload!FHotReloadModule::DoHotReloadInternal() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\developer\hotreload\private\hotreload.cpp:679]
UE4Editor_HotReload!FHotReloadModule::DoHotReloadCallback() [d:\buildfarm\buildmachine_++depot+ue4-releases+4.8\engine\source\developer\hotreload\private\hotreload.cpp:620]
UE4Editor_HotReload!TBaseRawMethodDelegateInstance<0,FHotReloadModule,TTypeWrapper<void> __cdecl(bool,enum ECompilationResult::Type),TArray<UPackage * ptr64,FDefaultAllocator>,TArray<FName,FDefaultAllocator>,FOutputDevice & ptr64>::Execute() [d:\buildfarm\buildmachine++depot+ue4-releases+4.8\engine\source\runtime\core\public\delegates\delegateinstancesimpl_variadics.inl:492]
UE4Editor_HotReload!TBaseRawMethodDelegateInstance<0,FHotReloadModule,void cdecl(bool,enum ECompilationResult::Type),TArray<UPackage * ptr64,FDefaultAllocator>,TArray<FName,FDefaultAllocator>,FOutputDevice & ptr64>::ExecuteIfSafe() [d:\buildfarm\buildmachine++depot+ue4-releases+4.8\engine\source\runtime\core\public\delegates\delegateinstancesimpl_variadics.inl:582]
UE4Editor_HotReload!FHotReloadModule::CheckForFinishedModuleDLLCompile() [d:\buildfarm\buildmachine
++depot+ue4-releases+4.8\engine\source\developer\hotreload\private\hotreload.cpp:1486]
UE4Editor_HotReload!FHotReloadModule::Tick() [d:\buildfarm\buildmachine
++depot+ue4-releases+4.8\engine\source\developer\hotreload\private\hotreload.cpp:433]
UE4Editor_UnrealEd!UEditorEngine::Tick() [d:\buildfarm\buildmachine
++depot+ue4-releases+4.8\engine\source\editor\unrealed\private\editorengine.cpp:952]
UE4Editor_UnrealEd!UUnrealEdEngine::Tick() [d:\buildfarm\buildmachine
++depot+ue4-releases+4.8\engine\source\editor\unrealed\private\unrealedengine.cpp:366]
UE4Editor!FEngineLoop::Tick() [d:\buildfarm\buildmachine
++depot+ue4-releases+4.8\engine\source\runtime\launch\private\launchengineloop.cpp:2359]
UE4Editor!GuardedMain() [d:\buildfarm\buildmachine
++depot+ue4-releases+4.8\engine\source\runtime\launch\private\launch.cpp:142]
UE4Editor!GuardedMainWrapper() [d:\buildfarm\buildmachine
++depot+ue4-releases+4.8\engine\source\runtime\launch\private\windows\launchwindows.cpp:126]
UE4Editor!WinMain() [d:\buildfarm\buildmachine
++depot+ue4-releases+4.8\engine\source\runtime\launch\private\windows\launchwindows.cpp:200]

I can’t decipher that either. :smiley: I see a hot reload in there, does the crash only happen after hot reloading? I just keep in mind that hot reloading isn’t perfect yet and can cause crashes.

I think that must be the root of the problem- maybe for some reason it was trying to hot reload the version without the bool== function (which also crashed)? In any event it works perfectly now, I really appreciate your time- I had no idea that you could customize a struct’s equality check like that.

You’re welcome. :slight_smile: