Circular dependency detected for filename

What is a ‘circular dependency’?

1>------ Rebuild All started: Project: DistantHome, Configuration: DebugGame_Editor x64 ------
1> Cleaning DistantHomeEditor Binaries…
1> Compiling game modules for hot reload
1> Compiling game modules for hot reload
1> Parsing headers for DistantHomeEditor
1>LogCompile : error : Circular dependency detected for filename D:\Library Storage\Documents\Unreal Projects\DistantHome\Source\DistantHome\Public\Game\DHGameInfo.h!
1> Reflection code generated for DistantHomeEditor
1> Performing 14 actions (2 in parallel)
1> [2/14] Resource ModuleVersionResource.rc.inl
1> PCH.DistantHome.h.cpp
1> [3/14] Resource PCLaunch.rc
1>d:\library storage\documents\unreal projects\distanthome\source\distanthome\Public/Game/DHGameInfo.h(43): error C3431: ‘EItemType’ : an unscoped enumeration cannot be redeclared as a scoped enumeration
1> -------- End Detailed Actions Stats -----------------------------------------------------------
1>ERROR : UBT error : Failed to produce item: D:\Library Storage\Documents\Unreal Projects\DistantHome\Binaries\Win64\UE4Editor-DistantHome-894-Win64-DebugGame.dll
1> Total build time: 61.86 seconds
1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V120\Microsoft.MakeFile.Targets(43,5): error MSB3075: The command ““C:\Program Files\Epic Games\4.8\Engine\Build\BatchFiles\Rebuild.bat” DistantHomeEditor Win64 DebugGame “D:\Library Storage\Documents\Unreal Projects\DistantHome\DistantHome.uproject” -rocket” exited with code 5. Please verify that you have sufficient rights to run this command.
========== Rebuild All: 0 succeeded, 1 failed, 0 skipped ==========


// Copyright 2015 Dirt Productions. All rights reserved.

#pragma once

#include "GameFramework/Info.h"
#include "Public/Item/Weapon/DHWeapon.h"
#include "DHGameInfo.generated.h"

/**
 * Class used to store game's enumerations, structures, and basically anything else that applies to the entire game.
 */
UCLASS()
class DISTANTHOME_API ADHGameInfo : public AInfo
{
	GENERATED_BODY()

};

// Type of match
UENUM(BlueprintType)
enum class EGameType : uint8
{
	SingleplayerNormal UMETA(DisplayName = "Campaign"),
	SingleplayerCustom UMETA(DisplayName = "AI Match"),
	MultiplayerNormal UMETA(DisplayName = "Multiplayer"),
	MultiplayerRanked UMETA(DisplayName = "Ranked Match")
};

// Available weapon slots
UENUM(BlueprintType)
enum class EWeaponSlot : uint8
{
	NoWeapon UMETA(DisplayName = "No Weapon"),
	PrimaryWeapon UMETA(DisplayName = "Primary Weapon"),
	SecondaryWeapon UMETA(DisplayName = "Secondary Weapon"),
	MeleeWeapon UMETA(DisplayName = "Melee Weapon"),
	SpecialWeapon UMETA(DisplayName = "Ability")
};

// Type of game item
UENUM(BlueprintType)
enum class EItemType : uint8
{
	Weapon UMETA(DisplayName = "Weapon"),
	SpecialWeapon UMETA(DisplayName = "Ability")
};

/**
 * Player faction data
 * 'SpectatorTeam' is to clear confusion between spectators on a separate team and temporary spectators on either ARC or BAT after they die.
 * 'NoFaction' is only used for teamless game modes.
 */
UENUM(BlueprintType)
enum class EPlayerFaction : uint8
{
	NoFaction UMETA(DisplayName = "No Faction"),
	SpectatorTeam UMETA(DisplayName = "Spectators"),
	ARC UMETA(DisplayName = "A.R.C."),
	BAT UMETA(DisplayName = "B.A.T.")
};

// Burst fire information
USTRUCT()
struct FWeaponBurstData
{
	GENERATED_USTRUCT_BODY();

	UPROPERTY()
		uint16 ShotsPerBurst;

	UPROPERTY()
		uint16 ShotsFired;

	UPROPERTY()
		float DelayPerShot;

	FWeaponBurstData()
	{
		ShotsPerBurst = 3;
		ShotsFired = 0;
		DelayPerShot = 0.09f;
	}

	bool IsValid()
	{
		if (ShotsPerBurst >= ShotsFired) return true;
		else return false;
	}
};

// Per-weapon ammo data
USTRUCT()
struct FWeaponAmmoData
{
	GENERATED_USTRUCT_BODY();

	UPROPERTY()
		uint16 MaxLoadedAmmo;

	UPROPERTY()
		uint16 MaxCarryAmmo;

	UPROPERTY()
		uint16 LoadedAmmo;

	UPROPERTY()
		uint16 CarryAmmo;

	FWeaponAmmoData()
	{
		MaxLoadedAmmo = 30;
		MaxCarryAmmo = 210;
		LoadedAmmo = 30;
		CarryAmmo = 150;
	}

	bool IsValid()
	{
		if (LoadedAmmo <= MaxLoadedAmmo && CarryAmmo <= MaxCarryAmmo) return true;
		else return false;
	}
};

// Per-special weapon data
USTRUCT()
struct FSpecialWeaponAmmoData
{
	GENERATED_USTRUCT_BODY();

	UPROPERTY()
		float RechargeTime;

	UPROPERTY()
		float UseTime;

	UPROPERTY()
		uint16 MaxLoadedAmmo;

	UPROPERTY()
		uint16 LoadedAmmo;

	FSpecialWeaponAmmoData()
	{
		RechargeTime = 30.0f;
		UseTime = 1.0f;
		MaxLoadedAmmo = 4;
		LoadedAmmo = 4;
	}

	bool IsValid()
	{
		if (LoadedAmmo <= MaxLoadedAmmo) return true;
		else return false;
	}
};

// Per-player ammo data
USTRUCT()
struct FPlayerAmmoData
{
	GENERATED_USTRUCT_BODY();

	UPROPERTY()
	struct FWeaponAmmoData PrimaryAmmo;

	UPROPERTY()
	struct FWeaponBurstData PrimaryBurst;

	UPROPERTY()
	struct FWeaponAmmoData SecondaryAmmo;

	UPROPERTY()
	struct FWeaponBurstData SecondaryBurst;

	UPROPERTY()
	struct FSpecialWeaponAmmoData SpecialAmmo;

	FPlayerAmmoData()
	{
		struct FWeaponAmmoData DefaultAmmo;
		DefaultAmmo.MaxLoadedAmmo = 30;
		DefaultAmmo.MaxCarryAmmo = 210;
		DefaultAmmo.LoadedAmmo = 30;
		DefaultAmmo.CarryAmmo = 150;

		struct FWeaponBurstData DefaultBurst;
		DefaultBurst.ShotsPerBurst = 3;
		DefaultBurst.ShotsFired = 0;
		DefaultBurst.DelayPerShot = 0.09f;

		struct FSpecialWeaponAmmoData DefaultSpecial;
		DefaultSpecial.LoadedAmmo = 4;
		DefaultSpecial.MaxLoadedAmmo = 4;
		DefaultSpecial.RechargeTime = 30.0f;
		DefaultSpecial.UseTime = 1.0f;

		PrimaryAmmo, SecondaryAmmo = DefaultAmmo;
		PrimaryBurst, SecondaryBurst = DefaultBurst;
		SpecialAmmo = DefaultSpecial;
	}

	EWeaponSlot GetBestWeapon()
	{
		if (PrimaryAmmo.IsValid()) return EWeaponSlot::PrimaryWeapon;
		else if (SecondaryAmmo.IsValid()) return EWeaponSlot::SecondaryWeapon;
		else return EWeaponSlot::MeleeWeapon;
	}

	bool IsValid(bool bCheckSpecialWeapon)
	{
		if (PrimaryAmmo.IsValid() && SecondaryAmmo.IsValid())
		{
			if (bCheckSpecialWeapon)
			{
				if (SpecialAmmo.IsValid()) return true;
				else return false;
			}
			else return true;
		}
		else return false;
	}
};

// Weapon information
USTRUCT()
struct FWeaponInfo
{
	GENERATED_USTRUCT_BODY();

	UPROPERTY()
		FName WeaponDisplayName;

	UPROPERTY()
		FString WeaponID;

	UPROPERTY()
		bool bIsCapableOfFullyAutomatic;

	UPROPERTY()
		EWeaponSlot IntendedSlot;

	UPROPERTY()
		struct FWeaponAmmoData DefaultAmmoData;

	FWeaponInfo()
	{
		WeaponDisplayName = "My Weapon";
		WeaponID = "AssaultRifle_01";
		bIsCapableOfFullyAutomatic = true;
		IntendedSlot = EWeaponSlot::PrimaryWeapon;

		struct FWeaponAmmoData DefaultAmmo;
		DefaultAmmo.MaxLoadedAmmo = 30;
		DefaultAmmo.MaxCarryAmmo = 210;
		DefaultAmmo.LoadedAmmo = 30;
		DefaultAmmo.CarryAmmo = 150;

		DefaultAmmoData = DefaultAmmo;
	}
};

// Special weapon information
USTRUCT()
struct FSpecialWeaponInfo
{
	GENERATED_USTRUCT_BODY();

	UPROPERTY()
		FString WeaponDisplayName;

	UPROPERTY()
		FString WeaponID;

	UPROPERTY()
	struct FSpecialWeaponAmmoData DefaultAmmoData;

	FSpecialWeaponInfo()
	{
		WeaponDisplayName = "My Special Weapon";
		WeaponID = "Special_01";

		struct FSpecialWeaponAmmoData DefaultAmmo;
		DefaultAmmo.LoadedAmmo = 5;
		DefaultAmmo.MaxLoadedAmmo = 5;
		DefaultAmmo.RechargeTime = 30.0f;
		DefaultAmmo.UseTime = 1.0f;

		DefaultAmmoData = DefaultAmmo;
	}
};

// Weapon inventory
USTRUCT()
struct FWeaponInventory
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY()
		ADHWeapon* PrimaryWeapon;

	UPROPERTY()
		ADHWeapon* SecondaryWeapon;

	UPROPERTY()
		ADHWeapon* MeleeWeapon;

	UPROPERTY()
		ADHWeapon* SpecialWeapon;

	FWeaponInventory()
	{
	}
};

// Player hitpoints
USTRUCT()
struct FPlayerHitPoints
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY()
		float MaxHitPoints;

	UPROPERTY()
		float CurrentHitPoints;

	FPlayerHitPoints()
	{
		MaxHitPoints, CurrentHitPoints = 100.0f;
	}

	bool IsAlive()
	{
		if (CurrentHitPoints <= MaxHitPoints && CurrentHitPoints > 0.0f) return true;
		else return false;
	}
};

Does DHWeapon include DHGameInfo.h?

Circular Dependency basically means that two Header files are trying to include each other, and therefore the compiler can’t work out which one to compile first.

It’s a bit of a pain to resolve since it means reworking your #includes slightly, but Forward Declarations can help with this a lot (but only work if you’re not calling functions on or accessing the declared class).

Here’s an example from my own code. AGESGame_DefaultGameMode needs to declare variables of type ‘AGESGame_State’, ‘AGESGame_Satellite’ and ‘AGESGame_Debris’. However - those classes already have the gamemode header in their own include files. In order to avoid a cirular dependency, I have to do this:



// Copyright(C) Stormtide 2015

#pragma once

#include "GameFramework/GameMode.h"
#include "GESGame_DefaultGameMode.generated.h"

// Forward Declarations
class AGESGame_State;
class AGESGame_Satellite;
class AGESGame_Debris;

UCLASS()
class GESGAME_API AGESGame_DefaultGameMode : public AGameMode
{
	GENERATED_BODY()

public:

	AGESGame_Satellite* GetFirstInactiveSatellite();
	AGESGame_Debris* GetFirstInactiveDebris();
}


I can then declare variables of those types in the Gamemode, but I can’t access or call functions on them. All it does is essentially tell the compiler that ‘at some point, these classes will exist’. - But the compiler doesn’t necessarily know what they are. Hope that explains it!

5 Likes

// Copyright 2015 Dirt Productions. All rights reserved.

#pragma once

#include "Public/Item/DHItem.h"
#include "DistantHome.h"
#include "DHWeapon.generated.h"

/**
 * Weapon subclass
 */
UCLASS()
class DISTANTHOME_API ADHWeapon : public ADHItem
{
	GENERATED_BODY()
	
	////////////////////** VARIABLES **////////////////////
public:
	EItemType ItemType;
    
	////////////////////** FUNCTIONS **////////////////////
public:
	// Sets default values for this weapon's properties
	ADHWeapon();
};

This is the code for DHWeapon
What should i forward declare in order for this to work?

I tried declaring enum class EItemType in DHGameInfo, but didn’t work
And I need DHWeapon.h to be included in DHGameInfo because of FWeaponInventory

Whenever I hit a point where I need circular dependencies, I make a public base class for the main one, leave the important members in it then include it in both headers that was trying to include each other; then everything works as if it was circular header inclusion and compiles just fine.