What is your preferred C / C ++ header policy for large projects?

When working on a large C / C ++ project, do you have certain rules regarding #include in source or header files?

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

  • #include is not allowed in .h files; it depends on each .c file to include all the headers it needs.
  • Each .h file should contain all its dependencies, that is, it should be able to compile independently without any errors.

I guess there is a trade-off between any projects, but what's yours? Do you have more specific rules? Or any link that claims to be for any of the solutions?

+47
c ++ c include coding-style header
Oct 08 '08 at 9:19
source share
13 answers

Running .h only includes C files, which means that if I just include the header (defining something that I want to use in the C file), it may not succeed. This may fail because I have to include 20 other headers. And even worse, I have to include them in the correct order . With lots of .h files, this system ends up being hellish hell in the long run. You just want to include one .h file in one .c file, and you will spend 2 hours to find out what other .h files you need and in what order you should include them.

If the .h file requires another .h file that must be successfully included in the C file, which does not contain anything other than this .h file and without causing compilation errors, I will include this other .h file in the .h file itself. Thus, I am sure that every C file can include every .h file, and it will never cause errors. A .c file should never worry about which other .h files to import or in what order to include them. This works even with huge projects (1000.h files and above).

On the other hand, I never include the .h file in another, if this is not necessary. For example. if I have hashtable.h and hashtable.c, and hashtable.c needs hashing.h, but hashtable.h does not need this, I do not include it there. I only include it in the .c file, since other .c files, including hashtable.h, also do not need hashing.h, and if they need them for any reason, they should include it.

+38
Oct 08 '08 at 10:14
source share

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

Include only the header files needed to compile the file, using only what is defined in this header. It means:

  • All objects present as references or pointers should only be declared forward.
  • Include all headers defining functions or objects used in the header itself.
+17
Oct 08 '08 at 9:32
source share

I would use rule 2:

All headers should be self-contained, whether it be:

  • uses nothing specific elsewhere
  • forward character declarations defined elsewhere
  • including headings identifying characters that cannot be declared forward.

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

Then, in the C / C ++ source file, specify only what is necessary: ​​if HeaderA forward-declared the character defined in HeaderB, and that you use this character, you will have to include both ... The good news: that if you do not use the forward -declared symbol, then you can only enable HeaderA and avoid including HeaderB.

Note that playing with templates makes this check "empty source, including your title, should compile" a little harder (and funny ...)

+13
Oct 08 '08 at 10:22
source share

The first rule will fail as soon as circular dependencies appear. Therefore, it can not be applied strictly.

(This can still be made to work, but it replaces most of the work from the programmer to the consumer of these libraries, which is obviously wrong.)

I’m all in favor of rule 2 (although it might be nice to include “direct ad headers” instead of the real deal, as in <iosfwd> , because it reduces compilation time). Actually, I think this is a kind of self-documentation, if the header file “declares” what dependencies it has, and what better way to do this than include the necessary files?

EDIT:

In the comments, I am challenged that circular dependencies between the headers are a sign of poor design and should be avoided.

It is not right. In fact, circular dependencies between classes can be inevitable and are not at all a sign of poor design. The examples are numerous, let me just mention the Observer pattern, which has a circular link between the observer and the subject.

To allow roundness between classes, you should use forward declaration, because the order of declaration matters in C ++. It is now perfectly acceptable to handle this direct declaration in a circular fashion in order to reduce the number of shared files and centralize the code. Of course, the following case does not deserve this scenario, because there is only one forward announcement. However, I worked on a library where it was 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 
+8
Oct 08 '08 at 9:22
source share

The minimum version of 2..h files includes only the header files that it specifically requires for compilation, using direct declarations and pimpl, as far as practicable.

+4
Oct 08 '08 at 10:15
source share
  • You always have title protection.
  • Do not pollute the global username space by placing any using namespace statements in the header.
+4
Oct 08 '08 at 11:36
source share

I would recommend going with the second option. You often find yourself in a situation where you want to add somwhing to a header file that suddenly requires a different header file. And with the first option, you have to go through and update many C files, sometimes not even under your control. With the second option, you simply update the header file, and users who don’t even need the new functionality that you just added do not even know that you did it.

+1
Oct 08 '08 at 9:43
source share

The first alternative (no #include in the headers) is dumb for me. I want to freely #include no matter what I may need, without worrying about manually #include with its dependencies. So, in general, I follow the second rule.

Regarding cyclic dependencies, my personal decision is to structure my projects in terms of modules, not classes. Inside the module, all types and functions can have arbitrary dependencies on each other. There may be no circular dependencies between the modules at all module boundaries. There is one * .hpp file and one * .cpp file for each module. This ensures that any forward declarations (necessary for circular dependencies that can only occur inside the module) in the header will ultimately always be resolved inside the same header. There is no need for headings for direct declaration only.

+1
Dec 14 '13 at 4:14
source share

Pt. 1 fails if you want to have precompiled headers through a specific header; eg. this is what StdAfx.h is for VisualStudio: you put all the common headers there ...

0
08 Oct '08 at 10:07
source share

It comes down to interface design:

  • Always pass by reference or pointer. If you are not going to check the pointer, pass by Help.
  • Go ahead and announce as much as possible.
  • Never use new ones in the class - create plants for this and pass them to the class.
  • Never use precompiled headers.

On Windows, my stdafx only ever includes afx ___. h headers - no library of strings, vectors, or accelerations.

0
Oct 08 '08 at 10:39
source share

Rule nr. 1, you will need to list your header files in a very specific order (including base class files that must go before including derived class files, etc.), which will easily lead to compilation errors if you make a mistake in the order.

The trick is that, as noted by several others, use as complex ads as possible, i.e. if references or pointers are used. To minimize dependencies, building dependencies in this way can be useful pimpl .

0
09 Oct '08 at 12:24
source share

I agree with Mecca, in short,

for each foo.h in your project include only those headers that are necessary to create

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

compilation.

(When using precompiled headers, they are of course allowed, for example, #include "stdafx.h" in MSVC)

0
Oct 26 '08 at 0:59
source share

Personally, I do this:
1 Perfer forward announces the inclusion of other .h files in the .h file. If something can be used as a pointer / link in these .h files or classes, forward declarations are possible without a compilation error. This may make the headers less dependent on dependencies (save compilation time? Not sure :().
2 Make .h files simple or specific. for example, it is bad to define all the constants in the CONST.h file, it is better to divide them into several such as CONST_NETWORK.h, CONST_DB.h. Therefore, in order to use one DB constant, it does not need to include other network information.
3 Do not put the implementation in the headers. Headings are used to quickly review public things for other people; when implementing them, do not pollute the declaration with details for others.

0
Dec 14 '13 at 4:03
source share



All Articles