Skip to content

Header Organization

Goal: Fast compilation, minimal coupling, clear dependencies

Include Order (MANDATORY)

// 1. Corresponding header (if .cpp file)
#include "audio_processor.hpp"

// 2. C standard library
#include <cmath>
#include <cstdint>

// 3. C++ standard library
#include <algorithm>
#include <memory>
#include <vector>

// 4. Third-party libraries
#include <juce_audio_basics/juce_audio_basics.h>

// 5. AudioLab foundation
#include <audiolab/types.hpp>

// 6. AudioLab core
#include <audiolab/core/buffer.hpp>

// 7. Local headers (same module)
#include "internal_helper.hpp"

Rationale: Detects missing includes in own header early

Header Guards

Use #pragma once:

#pragma once

// Header content

Why: Simpler than include guards, supported by all modern compilers

Header Content Structure

#pragma once

/**
 * @file filename.hpp
 * @brief Brief description
 *
 * Detailed description if needed.
 */

// Includes (ordered as above)
#include <...>

// Namespace
namespace audiolab {

// Forward declarations (if needed)
class SomeClass;

// Type aliases
using SomeAlias = int;

// Constants
constexpr int SOME_CONSTANT = 42;

// Class/struct declarations
class MyClass {
    // ...
};

// Inline function definitions (if small)
inline int small_function(int x) {
    return x * 2;
}

} // namespace audiolab

What Goes in Headers

✅ Always in Headers

  • Class declarations
  • Inline functions (small, hot path)
  • Template definitions
  • Constants (constexpr)
  • Type aliases
  • Enums

❌ Never in Headers

  • using namespace (pollutes namespace)
  • Large function definitions (unless template/inline)
  • Static variables with external linkage
  • Implementation details

Header vs Implementation (.cpp)

Header (.hpp)

#pragma once

class AudioProcessor {
public:
    void process(Sample* buffer, BlockSize size);

private:
    float calculate_gain();  // Declaration only

    float gain_;
};

Implementation (.cpp)

#include "audio_processor.hpp"

void AudioProcessor::process(Sample* buffer, BlockSize size) {
    // Implementation
}

float AudioProcessor::calculate_gain() {
    // Implementation
}

Inline Functions

When to inline: - Small (1-3 lines) - Hot path (called frequently) - Header-only libraries

class Sample {
    inline float to_db() const {
        return 20.0f * std::log10(value_);
    }
};

Template Headers

Templates MUST be in headers:

template<typename T>
class RingBuffer {
    void push(const T& value) {
        // Implementation in header
    }
};

Include What You Use (IWYU)

Only include what you actually use:

// ❌ Bad
#include <audiolab/audiolab.hpp>  // Includes everything

// ✅ Good
#include <audiolab/types.hpp>  // Only what's needed

Compilation Firewall (PIMPL)

For stable ABI, use PIMPL pattern:

// public_class.hpp
class PublicClass {
public:
    PublicClass();
    ~PublicClass();

    void do_something();

private:
    struct Impl;  // Forward declaration
    std::unique_ptr<Impl> impl_;  // Pointer to implementation
};
// public_class.cpp
struct PublicClass::Impl {
    // Private implementation details
    int some_data;
};

PublicClass::PublicClass() : impl_(std::make_unique<Impl>()) {}
PublicClass::~PublicClass() = default;

Remember: Headers are compiled many times. Keep them minimal.