Jump to content

  • Log In with Google      Sign In   
  • Create Account

C++ header inclusion question

  • You cannot reply to this topic
13 replies to this topic

#1 gcooper   Members   -  Reputation: 102

Like
0Likes
Like

Posted 20 October 2014 - 11:15 AM

So, consider the following code:

#ifndef HEADER1
#define HEADER1

#include "header2.h"

#endif

Keep in mind that header 2 also has header guards, and all my other headers in the project. However, when I compile the above code, I get an error saying that namespaces/classes that EXIST in header2 don't actually exist.

 

But then I tried an old trick:

#ifndef HEADER1
#define HEADER1

namespace namespace_in_header2{
	class class_i_need;
}

#endif

And after that I only included the actual header2.h in my header1.cpp. And it worked!

 

I'm sure that the first problem is caused somehow, by including header 2 again and again until chaos happens. But why? I mean all my header have header guards! Also, why including header2.h only in the cpp file fixes things? Wouldn't there still be a way in which it still gets added 2 or more times?

 

 



Sponsor:

#2 C0lumbo   Crossbones+   -  Reputation: 2273

Like
6Likes
Like

Posted 20 October 2014 - 11:46 AM

My guess is that you've accidentally used the same name for the include guards in both header1.h and header2.h

 

(as an aside, you should always use forward declarations when you can get away with it, not just as a last resort).



#3 frob   Moderators   -  Reputation: 21297

Like
5Likes
Like

Posted 20 October 2014 - 02:17 PM

Agreed about the duplicate include guard names. It is a fairly common error.
 
An alternate solution is to use:

#pragma once

 
The #pragma commands are system specific commands, but "#pragma once" support is very nearly universal, and is supported on all the major compilers.
 
There are a small number of cases where that fails, such as if a file exists twice in your source tree and is pulled in once from each location. Don't do that.

 

If you are sticking with inclusion guards, add some unique value beyond the file name. A guid works, the time you made the file, or even just a collection of random banging on the keyboard.  Something like #define header1_h_filemadebyfrob10202014

 

The older versions of Visual Studio used to ship with a macro that generated inclusion guards with both the file name and a guid to prevent duplication.  I would recommend using that type of macro... ... except the Visual Studio team decided to abandon the "best in class" software and is currently on a race-to-the-bottom of cutting critical features and making the non-essential features (like scanning the code for inline documentation) block the developer from important tasks (like code editing), and in their process of self-sabotage they removed macro support. If you are on VS2010 or earlier you can still have that option. Personally I recommend sticking with VS2008, the last version of Visual Studio where they actually cared about the developers, and plugging in a new compiler. Then you'll have that inclusion guard macro in the built-in collection.


Check out my personal indie blog at bryanwagstaff.com.

#4 Krypt0n   Crossbones+   -  Reputation: 2570

Like
0Likes
Like

Posted Yesterday, 04:13 AM

But why?

chances are that Header2 includes Header1 again. Maybe not directly, but by including something that includes something that..

if you're using visual studio, then there is a compile flag to show the includes, that way you might track down the recursion.

But then I tried an old trick:...
And after that I only included the actual header2.h in my header1.cpp. And it worked!

That's actually not a trick, that's the way you should preferably do it. Don't include stuff that you don't need, it might massively reduce your compile time once your project grows.
A lot of projects have build time issues and trying to solve it later on can be quite complicated. Just like you now might not know how the double-include happened, later on you might not know what path something should actually be include as everything includes everything. You might even run into issues where you cannot create a propper include order without cleaning it up (or creating really really bad hacks).

as a simple example, check your compile time if you include <windows.h> in some file, it's not affecting your program, just being processed. now imagin that for every single file (because at some point you will have such a big monster that might include other 'monsters' like directx/opengl, sound libs, network libs).

#5 SeraphLance   Members   -  Reputation: 1397

Like
4Likes
Like

Posted Yesterday, 07:00 AM

Some basic guidelines that can get you through most inclusion issues:

  1. Only include what you need.
  2. Only include headers in source files, never in other headers
  3. Use #pragma once.

Sometimes you'll need to deviate from these rules, but in the rare occasion that you do, you'll know.



#6 Quasimojo   Members   -  Reputation: 243

Like
4Likes
Like

Posted Yesterday, 06:09 PM

Some basic guidelines that can get you through most inclusion issues:

  1. Only include what you need.
  2. Only include headers in source files, never in other headers
  3. Use #pragma once.

Sometimes you'll need to deviate from these rules, but in the rare occasion that you do, you'll know.

 

Short of putting all headers in one huge header file, how can #2 be accomplished?  Not trying to be a wiseguy - I'm not well-versed in C++ and would honestly like to know.



#7 Madhed   Crossbones+   -  Reputation: 2969

Like
6Likes
Like

Posted Yesterday, 06:16 PM

I think #2 should actually read: "Only include headers in headers if you can't get away with forward declarations"



#8 SeraphLance   Members   -  Reputation: 1397

Like
0Likes
Like

Posted Yesterday, 07:31 PM

I think #2 should actually read: "Only include headers in headers if you can't get away with forward declarations"

 

Hence the "Sometimes you'll need to deviate from these rules, but in the rare occasion that you do, you'll know."  It's very rare that you can't get away with forward declarations.

 

 


Short of putting all headers in one huge header file, how can #2 be accomplished?  Not trying to be a wiseguy - I'm not well-versed in C++ and would honestly like to know.

 

As madhed mentioned above, forward declarations.  If header 2 contains a "Foo" class that you need to reference in header 1, just declare class foo; at the top of header 1.  If cpp file 1 includes header 1 AND header 2, the linker will take care of it.


Edited by SeraphLance, Yesterday, 07:31 PM.


#9 Ryan_001   Prime Members   -  Reputation: 1398

Like
0Likes
Like

Posted Today, 01:54 AM

 

I think #2 should actually read: "Only include headers in headers if you can't get away with forward declarations"

 

Hence the "Sometimes you'll need to deviate from these rules, but in the rare occasion that you do, you'll know."  It's very rare that you can't get away with forward declarations.

 

 


Short of putting all headers in one huge header file, how can #2 be accomplished?  Not trying to be a wiseguy - I'm not well-versed in C++ and would honestly like to know.

 

As madhed mentioned above, forward declarations.  If header 2 contains a "Foo" class that you need to reference in header 1, just declare class foo; at the top of header 1.  If cpp file 1 includes header 1 AND header 2, the linker will take care of it.

 

 

Its quite common to #include headers in header files.  Manual creation of forward declarations is gonna lead to lots of subtle errors as you now have your dependencies spread across many files.  Just use #pragma once at the top of every header file, include it where needed, and you won't have issues.  As far as #pragma once being non-portable, I know of no remotely modern C++ compiler that doesn't support it (http://en.wikipedia.org/wiki/Pragma_once#Portability).



#10 Juliean   GDNet+   -  Reputation: 2607

Like
0Likes
Like

Posted Today, 03:46 AM


Hence the "Sometimes you'll need to deviate from these rules, but in the rare occasion that you do, you'll know." It's very rare that you can't get away with forward declarations.

 

Unless you are using/writing templated classes. It's c++ after all, so templates are a (good) thing. Anything else though, should really be forward declared.

 


Its quite common to #include headers in header files.

 

Its also quite common to use i.e. the Singleton anti-pattern, that doesn't make it a good thing.

 


Manual creation of forward declarations is gonna lead to lots of subtle errors as you now have your dependencies spread across many files.

 

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.



#11 ChaosEngine   Crossbones+   -  Reputation: 2356

Like
2Likes
Like

Posted Today, 06:18 AM


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

#12 Zipster   Crossbones+   -  Reputation: 676

Like
0Likes
Like

Posted Today, 11:53 AM

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.


Edited by Zipster, Today, 12:40 PM.


#13 Ryan_001   Prime Members   -  Reputation: 1398

Like
0Likes
Like

Posted Today, 03:03 PM

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.

#14 ApochPiQ   Moderators   -  Reputation: 15703

Like
1Likes
Like

Posted Today, 03:22 PM

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.





PARTNERS