Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are include directives on top of header files?

Tags:

c++

c

include

I was once told in a programming class that C++ had achieved a better readability by letting the programmer declare its variable anywhere in a function block. This way, the variables were grouped together with the section of the code that dealt with it.

Why don’t we do the same for includes? Put differently, why is it discouraged to put the include file next to the definition that will actually use it?

parser::parser()
{
  // some initialization goes there which does not make use of regex
}

#include <boost/regex.hpp>
parser::start()
{
  // here we need to use boost regex to parse the document
}
like image 429
qdii Avatar asked Jul 27 '12 22:07

qdii


2 Answers

One of the reasons for this is that #include are context-less, they are just plain text inclusion, and having it in the middle of the code might have some unwanted effects. Consider for example that you had a namespace, and all your code in this file belongs to the namespace:

// Include on demand:
namespace ns {
   void f() {} // does not need anything
//... lots of other lines of code
#include <vector>
   void g() { std::vector<int> v; }
}

Now that might even compile fine... and wrong. because the inclusion is inside a namespace, the contents of the file are dumped inside ns and the included file declares/defines ::ns::std::vector. Because it is header only it might even compile fine, only to fail when you try to use that in an interface with a different subsystem (in a different namespace) -- This can be fixed, you only need to close all contexts, add the inclusion and reopen the same contexts...

There are other situations where the code in your translation unit might actually affect the includes. Consider, for example, that you added a using namespace directive in your translation unit. Any header included after that will have the using namespace directive in place, and that might also produce unwanted effects.

It could also be more error prone in different ways. Consider two headers that defined different overloads of f for int and double. You might add one (say the int version) towards the beginning of the file and use it, then add the other and use it. Now if you call f(5.0) above the line where the second header is included, the int version will be called --only overload available to the compiler at this point--, which would be a hard to catch error. The exact same line of code would have completely different meaning in different places in your file (admittedly that is already the case, but within the declarations in your file it is easier to find which is the one being picked up and why)

In general, the includes declare elements that you will be using in your component, and having them on the top provides a list of dependencies in a quick glance.

like image 171
David Rodríguez - dribeas Avatar answered Sep 23 '22 12:09

David Rodríguez - dribeas


Imagine the following file:

#include <someheader>

namespace myns {
  void foo() {
  }

  void bar() {
    // call something from someheader:
    func();
  }
}

It might be tempting to put #include <someheader> nearer the point of usage. That would be fine iff you wrote the following instead:

namespace myns {
  void foo() {
  }
}

#include <someheader>

namespace myns {      
  void bar() {
    // call something from someheader:
    func();
  }
}

The problem is in a medium/large sized file it's rather easy to loose track of quite how deeply nested you are inside namespaces (and other #ifdefs), depending on your indentation style. You might come back later and decide to move things around, or add another nested namespace.

So, if you write the #include at the top always you can never get bitten by accidentally writing something like:

namespace myns {
  void foo() {
  }

// Whoops, this shouldn't be inside myns at all!
#include <someheader>

  void bar() {
    // call something from someheader:
    func();
  }
}

which would be somewhere between wrong and very wrong depending on what exactly is in <someheader>. (You could for example end up with UB, by violating the ODR having multiple definitions which although otherwise legal and identical sequence of tokens match different functions and so violate § 3.2.5).

like image 44
Flexo Avatar answered Sep 21 '22 12:09

Flexo