Search code examples
c++includeheader-filescircular-dependency

Why can't header files include eachother?


Why can't I do something like this in C++?

A.h:

#ifndef A_H
#define A_H
#include "B.h"

struct A {
    int a;
};

void doStuff1 (B b);  // Error here

#endif

B.h:

#ifndef B_H
#define B_H
#include "A.h"

struct B {
    int b;
};

void doStuff2 (A a);  // Error here

#endif

I get an error that 'A' was not declared in this scope and the same with 'B'. I know about forward declaration, but I wanted to see if it was possible to have a set up like this as pass-by-value instead of by reference/pointer. Why does the compiler behave like this, if both A and B are in fact declared by the time the compiler reaches that code?


Solution

  • The basic lesson: Includes are processed before any C++ is parsed. They're handled by the pre-compiler.

    Let's say that A.h winds up being included prior to B.h. You get something like this:

    #ifndef A_H
    #define A_H
    
    // ----- B.h include -----    
    
    #ifndef B_H
    #define B_H
    #include "A.h" // A_H is defined, so this does nothing
    
    struct B {
        int b;
    };
    
    void doStuff2 (A a);  // Error here
    
    #endif
    
    // ----- B.h include -----
    
    struct A {
        int a;
    };
    
    void doStuff1 (B b);  // Error here
    
    #endif
    

    At this point, the C++ compiler can take over and start parsing things out. It will try to figure out what the parameter to doStuff2 is, but A hasn't been defined yet. The same logic holds true going the other way. In both cases, you have dependencies on types that haven't been defined yet.

    All of this just means that you have your dependencies out of order. It isn't a problem with pass-by-value. Your types must be defined prior to your methods. That's all - see the example below.

    // Example program
    #include <iostream>
    #include <string>
    
    // data_types.h
    struct A
    {
        int x;
    };
    
    struct B
    {
        int y;
    };
    
    using namespace std;
    // methods_A.h
    void foo(A a)
    {
        a.x = 3;
        cout << "a: " << a.x << endl;
    }
    
    // methods_B.h
    void bar(B b)
    {
        b.y = 4;
        cout << "b: " << b.y << endl;
    }
    
    int main()
    {
       A first;
       B second;
       first.x = 0;
       second.y = 100;
       foo(first);
       bar(second);
       cout << "A: " << first.x << ", B: " << second.y << endl;
    }
    

    Example output

    a: 3
    b: 4
    A: 0, B: 100