C++ Question bloomin' include hirearchy

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
11,318
Reaction score
2,786
Points
203
Location
between the planets
Seriously, I can't seem to find a solid description of how the include hierarchy in C++ exactly works. And I'm just blundering around and trying this and that, but I don't really get it.

For example:

I have a common.h header that declares most of the stuff that's used by most anything else, and includes the most important library headers. First off, it includes orbitersdk.h

Now, I thought it would be a nice thing to put an include of that file first in every source file, and right after that include the header of the sourcefile. This was recommended by several people on the net, although they seem to presupose that you're having an Idea of what you're doing to begin with and don't explain how the whole thing actually works.

Now, I have my main cpp file, the one with #define ORBITER_MODULE at the start. So I go on and include common.h first, and then the header file declaring the stuff that's in the cpp file itself.

As a result, it doesn't recognise anything from orbitersdk.h.

So I change it and include only the header of the source file, and in that header first include common.h, which includes orbitersdk.h. And it works.

So
source->common.h->orbitersdk.h->return to source->source.h
does not work.
while
source->source.h->common.h->orbitersdk->return to source.h
works fine.
I don't get the logic here. What about the order is actually important? obviously it's not the same if I include a file from a cpp file or from a header file. Why is it not the same? From where exactly must a header file be included and which other header files then have access to it? I'm completely lost here. Obviously it's not just a question of order, it's also a question of where you include from.

Could someone just give me a few clues here?
 
The text of all include files is simply attached to the cpp file by preprocessor before compilation in the order they were included, respecting all the directives controlling whether a part or whole file should be included or not (#pragma once; #if/n/def) and all used macros (#define) are replaced with their value.

If something hasn't been included but it should, it's most likely because of a conditional compiling preprocessor directive which is dependent on a macro set in the file included later.

You can check how files were preprocessed with /P switch of the Visual C++ compiler (CL).
 
What orb said. I'd also like to add that only cpp files are actually compiled. They get compiled into object files, which are then linked together by the linker to the executable (or DLL).

In theory, it is really that simple: CPP->Preprocessor->Compiler->Linker->EXE/DLL

However, the complex macro expansion mechanisms sometimes make it really complicated. Preprocessor pragmas only worsen this.

Your example, however, is hard to grasp without details. Why does it not work (error messages)? What exactly is in common.h? What files does your project consist of?
 
Last edited:
You example, however, is hard to grasp without details. Why does it not work (error messages)?

The error message is as if the include for orbitersdk.h wasn't there at all, i.e. the compiler has no idea what a VESSEL3 is. Structure is like this:

IMS.cpp:
Code:
#include "Common.h"
#include "IMS.h"

Common.h
Code:
#pragma once
#include "orbitersdk.h"
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include "utils.h"

IMS.h
Code:
#pragma once
class IMS2 : public VESSEL3
 
Common.h
Code:
#pragma once
#include "orbitersdk.h"
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include "utils.h"

According to the documentation, shouldn't you always define something before including "orbitersdk.h" ?
Like:
Code:
//...
#define ORBITER_MODULE
#include "orbitersdk.h"
//...
(defining ORBITER_MODULE is required! )

/Kuddel
 
Last edited:
(defining ORBITER_MODULE is required! )
It's only allowed for a single (let's call it main) object file in the module. Defining it in multiple objects would cause "symbol already defined" in another .obj, or something like that upon linking.

(Otherwise, if it was required before every time you include orbitersdk.h, then why wouldn't it be defined inside that header instead?)
 
(Otherwise, if it was required before every time you include orbitersdk.h, then why wouldn't it be defined inside that header instead?)

Martin uses this header as well to compile Orbiter and because he is using it "from the other side" he might not want that to be defined.

Anyway, this is what it enables/disables:

OrbiterAPI.h:
Code:
#ifdef ORBITER_MODULE
void dummy();
void calldummy () { dummy(); }
DLLCLBK char *ModuleDate () { return __DATE__; }
#endif
which really makes me :idk:, but anyway...

The "symbol already defined" should not happen because of the "#pragma once", or not?
I am not a fan of "#pragma once", I stick to the good old "#ifndef __FOO_H__ ..." things ;)



@jedidia :
I hope your IMS.h looks (at least) like this:
Code:
#include "Orbitersdk.h" // <= header is 'guarded', so save to "re-include"
                        // you have to include it if IMS.h is #included before "Orbitersdk.h" is included
                        // or VESSEL3 is not available here!
class IMS2 : public VESSEL3 {}; // <= class was not defined correct in prev. sample
...but I think you just cut too much before posting.

/Kuddel
 
Last edited:
The biggest problem with including every header you could possibly need in the main .h file of your project is this:

Your compile times will be excessive if you include more than you need.

Your runtimes may not be impacted because many compilers strip out unneeded code during the linking step, but your compiler step will take forever if you include more than you need.

A co-worker and I cleaned up the header files in a C++ project we were working on getting rid of all the unused headers that were included 'just in case' they were needed. Compile time went from 7.8 hours to 45 minutes. Then we got a hardware upgrade and the compile time went down to 15 minutes. Needless to say, management was happy.. and we both got laid off a few years later when the entire project was canceled... LOL.
 
The "symbol already defined" should not happen because of the "#pragma once", or not?

Or not. The #pragma once is to include the file only once in a single source / object file in a single compilation job. Each source cpp file is preprocessed and compiled into it's own object file separately. The directive won't prevent including a file in multiple separate cpp / object files which are created in separate compilation jobs, which then can be linked together by linker into an executable image, and which don't need to be even included in the same project (e.g. an external library).

ModuleDate and calldummy are global and would be defined in each object file which then are linked into the module. And this will cause LNK2005 error.

The calldummy function is required to link your module against Orbitersdk.lib's DllMain function, which initializes the DLL and calls InitModule and ExitModule functions if they were exported by your module. Without including it, you'd need to create your own DllMain DLL entry point which would initialize and clean the allocated resources.


You can't define ORBITER_MODULE in any source file for example if you create a static library (.lib) using Orbiter API functions.
 
I'd like to point out that we did not really address jedidia's question (and problem) yet.

If you think in terms of the simple pre-processor/compiler/linker chain, his example should compile just fine, because common.h is including orbitersdk.h, and this in term gets included before IMS.h . So why is it failing?

I think there is more at work here that we just don't know yet, because a similar configuration works for me in the AU project. There I also have 2 header files, with the first included including the orbitersdk.h, and the second declaring a class derived from an Orbiter-type (although not VESSEL3). The second header had an additional include of orbitersdk.h guarded with pragma once in it, but I took that out and it still compiles without errors.

So there must be another reason why it fails for you, jedidia.
 
Screw that (what I posted before), found the problem. There was one source file which contains functions of the class (so far, the class is distributed over three files) that did not include common.h before its own header. Whyever that let to errors in the header itself I don't know, but now that Common.h is included first in every cpp file that contains functions of this class, it works.
 
Last edited:
I was just going to post that the error messages come from another object compilation. That's why I've mentioned that only cpp files get compiled into objects.

This is a common mistake: header files don't do anything on their own. It is the cpp that includes them that gets compiled. And if you have more than one cpp including the same header (which is the whole reason why you should split it first place), things can start looking awkward when you only concentrate on the headers.

Of course your second cpp did not find the orbitersdk.h declarations, because it never included them.

Try to imagine the pre-processor operation: on every cpp compilation step, the pre-processor FIRST steps line by line through the cpp file. If it finds an #include line, it simply loads the file into memory, and proceeds that file's lines again step by step. This recursive inclusion happens (besides many more things like macro defines/expansions) until it reaches the end of the original cpp file. The resulting big intermediate file is then piped into the compiler, who happily fails on ignores everything outside the pure C/C++ specification. After all cpp files were processed like so, the linker comes into play, loads all objects into memory and thereby tries to assemble them into appropriate places as well as link together loose ends. The result is then one monolithic executable.

Of course modern compilers don't really have this explicit pre-processor step anymore, but it started like that, and IMHO it is still a good abstraction to get what happens inside.

Hope that helped,
Face
 
Last edited:
Whyever that let to errors in the header itself I don't know
Because the error isn't in the header itself, but in the preprocessed cpp file, which declared/defined the include file dependent symbols after the symbol was used in that file, or didn't declare it at all. Compiler shows where in file the location and the cause of the error is and not the source of the error (which may be in another file).
 
So in other words, the important thing is that every individual cpp file has the neccesary header files mentioned somewhere in its include path. I always thought it was kind of a "include once per project" deal, and you just make sure that the appropriate header is included somewhere in the include history before the cpp file that needs it gets loaded.

Thanks everyone. Seeing much, much, MUCH clearer now! :)
Only thing I still wonder is why I can search the net for "include hierarchy C++" and get a lot of good tips, but noone ever explains that...
 
Only thing I still wonder is why I can search the net for "include hierarchy C++" and get a lot of good tips, but noone ever explains that...

I'm so bold to blame IDEs for that. If you work with compilers on the command line, you realize the chain early enough. Unfortunately, many Windows developers these days have never seen a black screen with white text on it (and if so, they quickly typed "exit", or just copy&pasted some "magic spell" in there). The "gurus" on the net, however, assume that you know this chain already, because they are so used to it themselves.

Once you've seen the simple process behind it, all the complexity of IDE settings starts to make sense. Most of the time, it will just be another command-line parameter for the appropriate tool in the chain.
 
Sorry for being off-(main)-topic again
...The #pragma once is to include the file only once in a single source / object file in a single compilation job...
I knew that I had a reason to not like the "#pragma once" ;)
Anyway, I didn't realize that "#pragma once" has only a 'per module compile scope' (I have to find the correct terminus ;) ).
In this case the (re-)definition of ORBITER_MODULE will not be good.

Thanks for the update/info:tiphat:
/Kuddel
 
A "#pragma once" is just a preprocessor directive that tells the preprocessor to include the file it is in only once. Of course it is only active in one compilation step, a global "state" is never retained in any compiler I know of. If anything at all, the IDE might give certain pre-defines to specific (or all of the) compiler runs by means of command-line parameters.

The "#ifdef" trick is just the same as "#pragma once", with the only exception that some old compilers might not support the proprietary pragma. Macro defines are also only valid for the current compiler run (one cpp file), not for the global process.

That said, AFAIK "#pragma once" is faster than the "#ifdef" trick, because it will not even attempt to load the file into memory.
 
Back
Top