views:

2164

answers:

11

When working on a big C/C++ project, do you have some specific rules regarding the #include within source or header files?

For instance, we can imagine to follow one of these two excessive rules:

  1. #include are forbidden in .h files; it is up to each .c file to include all the headers it needs
  2. Each .h file should include all its dependancies, i.e. it should be able to compile alone without any error.

I suppose there is trade-off in between for any project, but what is yours? Do you have more specific rules? Or any link that argues for any of the solutions?

+6  A: 

The first rule will fail as soon as there are circular dependencies. So it cannot be applied strictly.

(This can still be made to work but this shifts a whole lot of work from the programmer to the consumer of these libraries which is obviously wrong.)

I'm all in favour of rule 2 (although it might be good to include “forward declaration headers” instead of the real deal, as in <iosfwd> because this reduces compile time). Generally, I believe it's a kind of self-documentation if a header file “declares” what dependencies it has – and what better way to do this than to include the required files?

EDIT:

In the comments, I've been challenged that circular dependencies between headers are a sign of bad design and should be avoided.

That's not correct. In fact, circular dependencies between classes may be unavoidable and aren't a sign of bad design at all. Examples are abundant, let me just mention the Observer pattern which has a circular reference between the observer and the subject.

To resolve the circularity between classes, you have to employ forward declaration because the order of declaration matters in C++. Now, it is completely acceptable to handle this forward declaration in a circular manner to reduce the number of overall files and to centralize code. Admittedly, the following case doesn't merit from this scenario because there's only a single forward declaration. However, I've worked on a library where this has been much more.

// observer.hpp

class Observer; // Forward declaration.

#ifndef MYLIB_OBSERVER_HPP
#define MYLIB_OBSERVER_HPP

#include "subject.hpp"

struct Observer {
    virtual ~Observer() = 0;
    virtual void Update(Subject* subject) = 0;
};

#endif


// subject.hpp
#include <list>

struct Subject; // Forward declaration.

#ifndef MYLIB_SUBJECT_HPP
#define MYLIB_SUBJECT_HPP

#include "observer.hpp"

struct Subject {
    virtual ~Subject() = 0;
    void Attach(Observer* observer);
    void Detach(Observer* observer);
    void Notify();

private:
    std::list<Observer*> m_Observers;
};

#endif
Konrad Rudolph
But why would you want circular dependencies between header files? That's a sign of poor API design.
dkagedal
My opinion exactly.
Dave Van den Eynde
Read the reply in my edit of the original post.
Konrad Rudolph
One might argue that the Observer pattern is a sign of bad design and should be avoided when not absolutely necessary. :)
bk1e
In your example of the observer pattern subject.cpp can forward include class Observer, and observer.cpp can forward include Subject. Then there is no need at all anymore to include the respective header files.
andreas buykx
+13  A: 

I think both suggested rules are bad. In my part I always apply:

Include only the header files required to compile a file using only what is defined in this header. This means:

  1. All objects present as reference or pointers only should be forward-declared
  2. Include all headers defining functions or objects used in the header itself.
PierreBdR
+1  A: 

I'd recommend going with the second option. You often end up in the situation where you want to add somwhing to a header file that suddenly requires another header file. And with the first option, you would have to go through and update lots of C files, sometimes not even under your control. With the second option, you simply update the header file, and the users who don't even need the new functionality you just added needn't even know you did it.

dkagedal
A: 

Pt. 1 fails when you would like to have precompiled headers through a certain header; eg. this is what StdAfx.h are for in VisualStudio: you put all common headers there...

Marcin Gil
+25  A: 

Doing .h includes only in C files means that if I just include a header (defining something I want to use in the C file) might fail. It might fail because I have to include 20 other headers upfront. And even worse, I have to include them in the right order. With a real lot of .h files, this system ends up to be an administrative hell in the long run. You just want to include one .h file in one .c file and you spend 2 hours to find out which other .h files you need and in which order you must include them.

If a .h file needs another .h file to be successfully included into a C file that does not include anything else but this .h file and without causing compile errors, I will include this other .h file in the .h file itself. That way I know for sure that every C file can include every .h file and it will never cause errors. The .c file never has to worry about which other .h files to import or in which order to include them. This works even with huge projects (1000 .h files and upwards).

On the other hand, I never include a .h file into another one, if this is not necessary. E.g. if I have hashtable.h and hashtable.c, and hashtable.c needs hashing.h, but hashtable.h doesn't need it, I don't include it there. I only include it in the .c file, as other .c files including hashtable.h don't need hashing.h either and if they need it for whatever reason, they shall include it.

Mecki
+2  A: 

A minimal version of 2. .h files include only the header files it specifically requires to compile, using forward declaration and pimpl as much as is practical.

David Sykes
+9  A: 

I would use the rule 2:

All Headers should be self-sufficient, be it by:

  • not using anything defined elsewhere
  • forward declaring symbols defined elsewhere
  • including the headers defining the symbols that can't be forward-declared.

Thus, if you have an empty C/C++ source file, including an header should compile correctly.

Then, in the C/C++ source file, include only what is necessary: If HeaderA forward-declared a symbol defined in HeaderB, and that you use this symbol you'll have to include both... The good news being that if you don't use the forward-declared symbol, then you'll be able to include only HeaderA, and avoid including HeaderB.

Note that playing with templates makes this verification "empty source including your header should compile" somewhat more complicated (and amusing...)

paercebal
A: 

This comes down to interface design:

  1. Always pass by reference or pointer. If you aren't going to check the pointer, pass by reference.
  2. Forward declare as much as possible.
  3. Never use new in a class - create factories to do that for you and pass them to the class.
  4. Never use pre-compiled headers.

In Windows my stdafx only ever includes afx___.h headers - no string, vector or boost libraries.

graham.reeds
Please justify points 1, 3, 4 (all of them, while you're at it). At the moment, they're pure nonsense.
Konrad Rudolph
+1  A: 
  1. Always have some sort of header guard.
  2. Do not pollute the user's global namespace by putting any using namespace statements in a header.
Ferruccio
A: 

Rule nr. 1 would require you to list your header files in a very specific order (include files of base classes must go before include files of derived classes, etc), which would easily lead to compilation errors if you get the order wrong.

The trick is, as several others have mentioned, use forward declarations as much as possible, i.e. if references or pointers are used. To minimize build dependencies in this way the pimpl idiom can be useful.

andreas buykx
A: 

I agree with Mecki, to put it shorter,

for every foo.h in your project include only those headers that are required to make

// foo.c
#include "any header"
// end of foo.c

compile.

(When using precompiled headers, they are allowed, of course - e.g. the #include "stdafx.h" in MSVC)

peterchen