5

Is there any valid reason for placing #include directives BEFORE the include guards in a header file like this:

#include "jsarray.h"
#include "jsanalyze.h"
#include "jscompartment.h"
#include "jsgcmark.h"
#include "jsinfer.h"
#include "jsprf.h"
#include "vm/GlobalObject.h"

#include "vm/Stack-inl.h"

#ifndef jsinferinlines_h___
#define jsinferinlines_h___

//main body mostly inline functions 

#endif

Note, this example is a taken from a real life high profile open source project that should be developed by seasoned programmers - the Mozilla Spidermonkey open source Javascript engine used in Firefox 10 (the same header file also exists in the latest version).

In high profile projects, I expect there must be some valid reasons behind their design. What are the valid reasons to have #include before the include guard? Is it a pattern applicable to inline-function-only header files? Also note that this header file (jsinferinlines.h) is actually including itself through the last #include "vm/Stack-inl.h" (this header file includes a lot of other headers and one of them actually includes this jsinferinlines.h again) directive before the include guard, this makes even less sense to me.

JavaMan
  • 4,954
  • 4
  • 41
  • 69

2 Answers2

4

Because you expect those headers to have include guards of their own, therefore it doesn't really make any difference.

Also note that this header file (jsinferinlines.h) is actually including itself through the last #include "vm/Stack-inl.h" (this header file includes a lot of other headers and one of them actually includes this jsinferinlines.h again) directive before the include guard, this makes even less sense to me.

It won't make any different because the workflow, when including that file, will be:

  • include all header up to "vm/Stack-inl.h"
  • include the "vm/Stack-inl.h" up to the last #include "jsinferinlines.h" (your file)
  • include the file jsinferinlines.h again (skipping all the previous includes, because include guards)
  • reach past #include "vm/Stack-inl.h"
  • finally process from #ifndef jsinferinlines_h___ on

But in general, mutual header recursion is bad and should be avoided at all costs.

Shoe
  • 74,840
  • 36
  • 166
  • 272
  • It has a pretty big impact on compilation performance, since it will have to fopen all those files first. (Of course real compilers would most often cache them, but still.) – berkus Apr 07 '14 at 09:22
  • 1
    What if we have two files including each other? (I am not discussing here how bad the practice is). In normal usage of include guards, when they are the first thing in the file, nothing will happen. If includes are before the guards, compiler will enter infinite loop. – Wojtek Surowka Apr 07 '14 at 09:25
  • @WojtekSurowka, of course. That's a mistake on your side though. – Shoe Apr 07 '14 at 09:29
  • @berkus, all modern compilers keep track of the files they include. – Shoe Apr 07 '14 at 09:29
  • 1
    @Jeffrey - and what modern compilers do if a file includes itself? – Wojtek Surowka Apr 07 '14 at 09:32
  • @Wojtek: just speculating, but could this actually a way of avoiding circular inclusions? – anderas Apr 07 '14 at 09:33
  • @anderas doesn't seem so to me. Forward declarations are a way to avoid circular inclusions. – berkus Apr 07 '14 at 09:35
  • 2
    @WojtekSurowka: A modern compiler will detect common "include guards" pattern and optimize the call to `fopen` accordingly. Legitimate uses of inclusion should work as if the optimization did not take place (otherwise it's a bug), and errors (infinite cycles, etc...) should be detected (otherwise it's a bug). Certainly modern versions of gcc and clang should handle those edge cases well. – Matthieu M. Apr 07 '14 at 09:35
  • @anderas - it may be. My point is that, contrary to Jeffrey's answer, there is a difference. For mutually including files and proper include guards nothing happens. With include guards as in the question we have infinite loop or a compiler error. So there is a difference. – Wojtek Surowka Apr 07 '14 at 09:37
  • @WojtekSurowka and berkus: sorry for being unclear. If it is a policy to always include before the guards, code that introduces circular inclusions wouldn't compile. This would force the developer to think about avoiding this, i.e. use forward declarations. – anderas Apr 07 '14 at 09:37
  • @anderas - this was my thinking too. But the question is - the OP says that the file includes itself (indirectly). Why those sources compile then? – Wojtek Surowka Apr 07 '14 at 09:39
1

There were lots of include cycles in SpiderMonkey headers back then, and placing the header guard at the top caused difficult to understand compile errors - I suspect that putting the header guard below the includes was just an incremental step toward sanitizing the includes.

I couldn't tell you the exact sequence of includes that caused it to make a difference, though.

Nowadays there shouldn't be any include cycles in SM headers, and their style is enforced through a python script written by Nicholas Nethercote. If you look at jsinferinlines.h today you'll see the header guard is at the top where it belongs.