Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How are circular #includes resolved?

Tags:

c++

c

In c lets say we have 2 files

1.h

#include<2.h>

blah blah

and we have 2.h

#include<1.h>

code

How is this resolved??

like image 687
Laz Avatar asked Nov 28 '22 11:11

Laz


2 Answers

Typically you protect your include file with an ifndef/define that corresponds to the file name. This doesn't prevent the file from being included again, but it does prevent the contents (inside the ifndef) from being used and triggering the recursive includes again.

 #ifndef HEADER_1_h
 #define HEADER_1_h

 #include "2.h"

 /// rest of 1.h

 #endif

 #ifndef HEADER_2_h
 #define HEADER_2_h

 #include "1.h"

 //  rest of 2.h

 #endif
like image 187
tvanfosson Avatar answered Dec 13 '22 09:12

tvanfosson


Okay, for the sake of completeness I'll begin by quoting tvanfosson's answer:

You should use include guards:

// header1.hpp
#ifndef MYPROJECT_HEADER1_HPP_INCLUDED
#define MYPROJECT_HEADER1_HPP_INCLUDED

/// Put your stuff here

#endif // MYPROJECT_HEADER1_HPP_INCLUDED

However include guards are not meant to solve circular dependencies issues, they are meant to prevent multiple inclusions, which is quite different.

          base.h
        /        \
    header1.h  header2.h
        \        /
         class.cpp

In this case (quite common), you only want base.h to be included once, and that's what include guards give you.

So this will effectively prevents the double inclusion... but you won't be able to compile!!

The problem can be illustrated by trying to reason as the compiler does:

  • Include "header1.hpp" > this defines the include guard
  • Include "header2.hpp
  • Try to include "header1.hpp" but doesn't because the include guard is already defined
  • Cannot correctly parse "header2.hpp" because the types coming from "header1.hpp" have not been defined yet (since it was skipped)
  • Back to "header1.hpp", and the types from "header2.hpp" are still missing since they could not be compiled, and thus it fails here too

In the end, you'll left with a big pile of error messages, but at least the compiler does not crash.

The solution is to somehow remove the need for this circular dependency.

  • Use forward declarations if possible
  • Split "header1.h" into 2 parts: the part independent from header2 and the other, you should only need to include the former in header2

And THIS will solve the circular dependency (manually) there is no magic going on in the compiler that will do it for you.

like image 41
Matthieu M. Avatar answered Dec 13 '22 09:12

Matthieu M.