C++ header inclusion question

Started by
17 comments, last by Juliean 9 years, 6 months ago


Care to elaborate how this is a bad thing? I never ever had an error directly related to forward declaration, and having depencies spread is really a good thing - now if I have to include "X.h" into "Y.h" I can be asured that Y only has the barely minimum depencies that X relies on in its declaration, mostly due to templates/typedefs. Instead of having Y.h now suddenly contain every single include that might be there in the include-chain, which gives you nothing but increased compile times. Even with #pragma once, having all header-files included in other header-files will still lead to recompilation of half your project if you make a change to the header of certain classes.

Essentially DRY.

If you have a class (or more likely templated typedef) that you commonly forward declare, it's a good idea to put the forward declaration in a separate header file (AwesomeClass_fwd.h) and include that.

As far as SeraphLances advice is concerned, I would say that including headers in headers is common enough to not be considered a deviation.

It's like saying "when driving, it's safest to stay below 20mph". It's certainly true, but not really practical. You're going to break that rule as soon as you want to do anything non-trivial.

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
Advertisement

As your project grows, more and more of your build time is going to be spent waiting for the preprocessor to expand #include's. We actually had to implement an automated single compilation unit solution for our project before the engineers started pulling their hair out waiting for builds to finish after making changes in middle- to low-level libraries. Your compile times technically increase, because you're now compiling a handful of large translation units, but the savings from not having to perform as much disk I/O cut our build times in half. And the engineers didn't mutiny.

EDIT: I should point out that we were already using IncrediBuild, which also benefited from fewer translation units.

Care to elaborate how this is a bad thing? I never ever had an error directly related to forward declaration, and having depencies spread is really a good thing - now if I have to include "X.h" into "Y.h" I can be asured that Y only has the barely minimum depencies that X relies on in its declaration, mostly due to templates/typedefs. Instead of having Y.h now suddenly contain every single include that might be there in the include-chain, which gives you nothing but increased compile times. Even with #pragma once, having all header-files included in other header-files will still lead to recompilation of half your project if you make a change to the header of certain classes.


If I include "X.h" into "Y.h" I would assume both "X.h" and "Y.h" only include what they in turn need. There are going to be dependencies that are required. Whether they are forward declared or they are included is of little difference as far as compilation times are concerned, but when it comes to bug hunting having 1 declaration in 1 file, as opposed to 50 declaration spread over 50 files, will be far easier to manage.

Now what you seem to be implying is that I'm suggesting to dump every header, in every other header, in some massive 'include everything under the sun' paradigm. That is not the case. Each file should be a logical unit or module that handles one or a few related concepts. If your code base is such that manually declaring things in each file results in significantly faster compilation times than including just those headers that are needed, then the problem is with the code base.

To be honest the recommendation to 'always forward declare' and not use #include seems so absurd that I get the feeling something has been misinterpreted, and that this post has somehow gotten off tangent and we aren't actually discussing the same thing.
Forward declares make a real, measurable compilation speed difference on a lot of nontrivial codebases I've worked on. So it's hardly "absurd" to suggest that they be used when possible.

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]


If your code base is such that manually declaring things in each file results in significantly faster compilation times than including just those headers that are needed, then the problem is with the code base.

Not necessarily. Dependency chains are naturally longer in higher-level code than they are in lower-level code, even if you only ever include precisely what is needed.


but when it comes to bug hunting having 1 declaration in 1 file, as opposed to 50 declaration spread over 50 files, will be far easier to manage.

Are we talking about the same thing?

Class declaration:


class Foo {
    int bar;
}

Forward declaration:


class Foo;

The only thing that needs to be "managed" across 50 files is the name of the class. And if that changes you would have to change the files were it is used either way.

but when it comes to bug hunting having 1 declaration in 1 file, as opposed to 50 declaration spread over 50 files, will be far easier to manage.


Are we talking about the same thing?

Class declaration:

class Foo {
    int bar;
}
Forward declaration:

class Foo;
The only thing that needs to be "managed" across 50 files is the name of the class. And if that changes you would have to change the files were it is used either way.


I'm not trying to be argumentative here, I am truly confused at some of the responses to this post. Apart from the odd circular definition, when do you need one and not the other? I can't think of many places where I need a pointer to an object but never call or access the object in any way?

Though to more directly answer your question, I was more thinking for things like function declarations, enumerations, constants, ect...

but when it comes to bug hunting having 1 declaration in 1 file, as opposed to 50 declaration spread over 50 files, will be far easier to manage.


Are we talking about the same thing?

Class declaration:

class Foo {
    int bar;
}
Forward declaration:

class Foo;
The only thing that needs to be "managed" across 50 files is the name of the class. And if that changes you would have to change the files were it is used either way.


I'm not trying to be argumentative here, I am truly confused at some of the responses to this post. Apart from the odd circular definition, when do you need one and not the other? I can't think of many places where I need a pointer to an object but never call or access the object in any way?

Though to more directly answer your question, I was more thinking for things like function declarations, enumerations, constants, ect...

Unless you're dumping implementation code in your headers (which you generally shouldn't do, excluding templates)...

If you are using an object that stores a pointer to a Foo you do not need to know how Foo is defined, simply that there is a type "Foo". You only need to know more about Foo iff you happen to use Foo to do something. If your code is well behaved then you will have encapsulated much of the behavior (and the fact that there IS a Foo) away (Even with data driven code this is still important).

Furthermore, you do not need to know what a Foo is, just that there is a Foo, to store a pointer or reference to a Foo in a container type. i.e. std::vector<Foo*>. Although, I would recommend some variation of smart pointer in that case, tbh.

In time the project grows, the ignorance of its devs it shows, with many a convoluted function, it plunges into deep compunction, the price of failure is high, Washu's mirth is nigh.


I'm not trying to be argumentative here, I am truly confused at some of the responses to this post. Apart from the odd circular definition, when do you need one and not the other? I can't think of many places where I need a pointer to an object but never call or access the object in any way?

Thats why the suggestion is not to include a header of any class where you only need the declaration in another header file. As washu has said, consider this situation:


// Foo.h

class Foo
{
public:
	void func();
}

// Bar.h

// at this point, we don't need to know anything of the foo-class other than that it exists
class Foo;

class Bar
{
public:
	
	void func();
	
private:
	Foo* foo;
}

// Bar.cpp
#include "Foo.h" // at this point, we need the full definition of Foo

void Bar::func()
{
	foo->func();
}

This topic is closed to new replies.

Advertisement