Download

How do you guys combat circular dependencies in your file heads?

I have two classes, ATank and UBuff. ATank holds a TArray of UBuff to keep track of every active reference of UBuff that is owned by ATank and UBuff holds a reference to its owner ATank so it can invoke methods or access properties. This causes circular dependency if I include each other’s header in their header, but how else can I structure this so that these two classes know what each other are?

I have temporarily solved this by only forward declaring UBuff in ATank, but what if at some point ATank needs to invoke a method on UBuff. For example a cleanse effect that would call the Destroy method on UBuff. Holding refs to each other in blueprint seems possible while invoking methods and etc, but I’m doing this in code because I want more flexible use out of FTimerHandle which is what I’m using for buff timers.



///* Project/Public/Tank.h */

#pragma once


//#include "Buffs/Buff.h"

#include "GameFramework/Pawn.h"
#include "Tank.generated.h"

UCLASS()
class PASTRYPANZERPANIC_API ATank : public APawn
{
	GENERATED_BODY()

public:

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Instanced, Category="Tank")
	TArray<class UBuff*> activeBuffs;

public:
	// Sets default values for this pawn's properties
	ATank();

	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	// Called every frame
	virtual void Tick( float DeltaSeconds ) override;

};




///* Project/Public/Buffs/Buff.h */

#pragma once

#include "../Tank.h"

#include "UObject/NoExportTypes.h"
#include "Buff.generated.h"

UCLASS()
class PASTRYPANZERPANIC_API UBuff : public UObject
{
	GENERATED_BODY()

public:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
	float lifeSpan;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
	float timeCreated;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
	float tickRate;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
	float timeActive;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
	bool bCanExpire;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Buff", Meta=(ExposeOnSpawn=true))
	class ATank *owningTank;

public:

	UBuff();

	UFUNCTION(BlueprintCallable, Category="Buff")
	virtual void OnCreation();

	UFUNCTION(BlueprintCallable, Category="Buff")
	virtual void OnTick();

	UFUNCTION(BlueprintCallable, Category="Buff")
	virtual void OnDestroy();


};




///* Project/Public/Buffs/Buff.cpp

#include "PastryPanzerPanic.h"
#include "Buff.h"

UBuff::UBuff(){

}

void UBuff::OnCreation(){

   // FTimerHandle buffTimer;
   //
   // if(tickRate > 0.f){
   //    GetWorldTimerManager().SetTimer(buffTimer, this, &UBuff::OnTick, tickRate, true);
   // }
   //
   // owningTank->activeBuffs.Add(this);
   //
   // GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor:Blue, "Buff Created.");
}

void UBuff::OnTick(){
   // GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor:Yellow, "Buff Ticking.");
   // timeActive += tickRate;
   // if(timeActive > lifeSpan){
   //    this->OnDestroy();
   // }
}

void UBuff::OnDestroy(){
   // GEngine->AddOnScreenDebugMessage(-1, 15.0f, FColor:Red, "Destroy Called.");
   // GetWorldTimerManager().ClearTimer(buffTimer);
   // owningTank->activeBuffs.Remove(this);
   // Destroy();
}

I would use PlayerState (from now PS ) as middleman, since PS has Controller bound hence you can get the pawn, plus it makes sense to store buffs there for replication purposes (if you have multiplayer). So you would call PS to get your UBuffs or call PS to get the Pawn with cast to your UTank. These Gameplay classes are there for a reason :slight_smile:

I’m not sure I 100% agree with storing these kinds of data structures there. Why would it make more sense to put a data structure containing unit buffs on something global like the Player State and not the unit itself? Not everything that will be affected by these buffs will be player controlled. I’m not new to programming, but I have 0 experience with how to manage gameplay logic like what you would find in MMO and RTS games.

I solved my problem. Forward declension is all that’s needed in the class header. I move the header include for my class references to the source file which solved the circular dependency issue.

In case forward declaration is not enough, I do what I like to call ‘triangular reference’.
Add a “man in the middle” class, make ATank_Base with include for UBuff, UBuff includes ATank which is child of _Base.

Bruno:

Since a forward declaration is just a name which the compiler recognises, why wouldn’t that be enough?

You may run into “incomplete type” issues if you want to access member functions.

Therefore you ‘forward’ the type.

Usage: in header.h


class forwardedClass;

class UsingIt
{
public:
    forwardedClass* instance;
}

or:


class UsingIt
{
public:
    class forwardedClass* instance;
}


Notice they are pointers.

When you include the header at the source file which uses the forewardedClass, the type gets completed.

Unless you implement the definition in the header, which is sometimes the case, then you are right. Although, in that case you should move the definition to the sourcefile aswell.

The only reason (for me) to implement definitions into a header is just lazyness.

Yeah, I am lazy…
But that is not a bug on me, it’s a feature :stuck_out_tongue:

I know this is a long-dead thread, but FWIW, I like Bruno Xavier’s approach better than Forward Declarations. Not to mention it seems more obvious and requires less explanation. At least for me. For those that can’t visualize what I mean, here it goes.

Let’s say you have Inventory.h and Character.h. One is referencing each other. That causes a circular dependency. The approach Bruno mentioned is pretty much “injecting” a man-in-the-middle that will do the communication for you. So alongside the headers I mentioned, you can create a third file called something like Manifest.h and include the circle in there. I.e.:



// Manifest.h

#pragma once

#include "Inventory.h"
#include "Character.h"




// Inventory.h

#pragma once

#include "Manifest.h"

// ...




// Character.h

#pragma once

#include "Manifest.h"

// ...


As simple as that!

I’m registering this here because it might help others as well as myself in the future.

Cheers!

By the way, Bruno, do you know any implications of your approach? Thanks!

These days several portions of Unreal itself is doing this.
“Something_Base.h” in Unreal source has become very common practice.

No serious collateral damage.

Make a IBuff interface (abstract class) which will have all the methods you need, keep an array of those in tank, concrete implementation will include whatever it needs. Simple as that.

Try make forward delcaration for all classes you need in .h file, and do final include in .cpp file works file!~

Forward Declaration is the way to go indeed :slight_smile: