Skip to main content

08 / 16

Headers, IWYU, and modules

Include what you use, keep headers self-contained, and keep private dependencies private.

Include What You Use makes each file declare the definitions it needs instead of borrowing them from a unity build. Module rules then decide which of those dependencies the rest of the project inherits.

Rules

  1. 01Start every header with #pragma once.
  2. 02Keep .generated.h as the final include in any reflected header.
  3. 03Put the matching header first in a source file.
  4. 04Include the narrowest defining header. Do not rely on a transitive include, PCH, or unity build.
  5. 05Forward declare pointer and reference types when a complete definition is not required.
  6. 06Place implementation-only headers and every .cpp file in the module's Private directory.
  7. 07Epic sets PCHUsage = UseExplicitOrSharedPCHs so that no source file includes a module PCH by hand. Each .cpp includes its matching header instead, which is what makes a missing include surface as a compile error rather than a silent dependency.
  8. 08A module belongs in PublicDependencyModuleNames only when its types appear in your public headers. Unreal Build Tool defines the private list as modules "our private code depends on but nothing in our public include files depend on."
  9. 09Forward declaration works for pointers and references. A by-value member, a base class, or anything that needs sizeof requires the full definition.
  10. 10A header that compiles only because another header included something first will break the moment a unity build regroups the files. Compile each header on its own to catch that early.

Make every header self-contained

The header includes its base class and forward declares member pointer types.

Pickup.hcpp
#pragma once

#include "GameFramework/Actor.h"
#include "Pickup.generated.h"

class UItemDefinition;
class USphereComponent;

UCLASS()
class ACTIONGAME_API APickup final : public AActor
{
    GENERATED_BODY()

private:
    UPROPERTY(VisibleAnywhere)
    TObjectPtr<USphereComponent> CollisionComponent;

    UPROPERTY(EditDefaultsOnly)
    TObjectPtr<UItemDefinition> ItemDefinition;
};
Why
The header cannot accidentally depend on a unity build or precompiled header to provide a missing definition.
Costs
Forward declarations stop working when a complete type is required, such as a by-value member or some template instantiations.
Instead
Include the exact defining header when the compiler needs the type layout. CoreMinimal.h is convenient for game headers, but it does not replace feature-specific includes.

Include definitions where they are used

The matching header comes first. The source file then includes each concrete type used by its implementation.

Pickup.cppcpp
#include "Items/Pickup.h"

#include "Components/SphereComponent.h"
#include "Items/ItemDefinition.h"

APickup::APickup()
{
    CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("Collision"));
    SetRootComponent(CollisionComponent);
}
Why
Putting the matching header first catches missing dependencies in that header. Specific includes also prevent one broad header from rebuilding unrelated code.
Costs
The include list is longer and needs maintenance. In return, every dependency stays visible.
Instead
A private implementation type can hide unstable dependencies behind TUniquePtr, though it adds an allocation and another type. Use it when the compile-time boundary saves real rebuild time.

Keep private module dependencies private

A module becomes a public dependency only when its types appear in a public header.

ActionGame.Build.cscsharp
using UnrealBuildTool;

public class ActionGame : ModuleRules
{
    public ActionGame(ReadOnlyTargetRules Target) : base(Target)
    {
        PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

        PublicDependencyModuleNames.AddRange(new[]
        {
            "Core",
            "CoreUObject",
            "Engine"
        });

        PrivateDependencyModuleNames.AddRange(new[]
        {
            "AIModule",
            "GameplayTags",
            "Slate"
        });
    }
}
Why
Other modules inherit fewer include paths and link requirements. Changes to a private dependency also rebuild less of the project.
Costs
Moving one of those types into a public header requires updating the module rules. The resulting build error points out that the public API changed.
Instead
Use a public dependency whenever a public header includes or exposes the module. A private dependency cannot hide a real public compile requirement.

All 16 rules