Is requiring a certain order for #includes in c++ a sign of bad library/header design?
Asked Answered
Z

13

12

I've used some very large scale systems and never seen a required order, but came across it recently. Does the STL or STD library or even Boost have any cases where certain includes must come in a certain order?

Zita answered 17/12, 2008 at 19:2 Comment(0)
U
28

Does the STL or STD library or even Boost have any cases where certain includes must come in a certain order?

For the standard, the answer is emphatically, no. I imagine the same is true for Boost, though I haven't looked it up.

From the C standard:

Standard headers may be included in any order; each may be included more than once in a given scope, with no effect different from being included only once, except that the effect of including <assert.h> depends on the definition of NDEBUG (see 7.2).

the C++ standard has similar wording.

My preference is that headers should include their own dependencies, but I've worked with people who believe this to be 'wasteful'. In my opinion, not having headers include their dependencies is a worthless early optimization.

Unripe answered 17/12, 2008 at 19:13 Comment(2)
Especially since it's easy enough to put guard conditions on a header so that they are only ever included once, making the "optimization" completely useless.Ahmad
Agree. Compilers can optimize away the extra includes and if you skip them you will get into a mayhem if one of your included headers changes into not including something you need. Forward declarations aside, when they apply, that is.Predecessor
H
9

This definitely sounds like a bad design. If somehow a specific order was required, the library should provide one header that includes the other ones in the correct order.

As far as boost, and the STL, I'm pretty sure I haven't encountered this situation yet.

Hydrozoan answered 17/12, 2008 at 19:5 Comment(0)
G
9

Needing to specify includes in a specific order almost always indicated a design problem. One way to reduce the possibility of inadvertantly doing this, is to get into the practice of including a class's header file as the first #include in the implementation file.

// A.cpp
#include "A.h"
#include "boost/shared_ptr.hpp"
#include <vector>

class A {
// ...
};

This way if, for example, A.h uses a vector without the right #include, A.cpp won't compile.

I can't remember where I picked this up; it might have been from "Large Scale C++ Design" by Lakos (a great book that could really use an update).

Guileless answered 17/12, 2008 at 19:58 Comment(0)
G
3

Does the STL or STD library or even Boost have any cases where certain includes must come in a certain order?

I have never come across this and if it is so then the authors must be notified ASAP. And oh yes its a very bad design.

Guttle answered 17/12, 2008 at 19:5 Comment(1)
+1: bad smell. And there's a standard #ifdef sandwich to assure that all .h's include all their proper dependencies.Sump
H
3

That is a 'bad thing'. A better way has been mentioned; but I'll elaborate.

//a.h
#ifndef _A_H_
#define _A_H_

//... code ...

#endif
// -----------------
//b.h
#ifndef _B_H_
#define _B_H_
#include a.h

//... code ...

#endif
// -----------------
//main.cpp Try 1
#include "b.h" //<- okay!  b includes a, then does b
// -----------------
//main.cpp Try 2
#include "a.h" //<- includes a
#include "b.h" //<- okay!  b includes a, but skips redefining it, then does b
// -----------------
//main.cpp Try 3
#include "b.h" //<- b includes a, then does b
#include "a.h" //<- okay!  a skips redefining itself!
// -----------------
//main.cpp Try 4
#include "a.h" //<- fail!  b is not included anywhere =(
Hermit answered 17/12, 2008 at 19:11 Comment(1)
Hmmm, I think you messed up your 'a's and 'b's. In line 5, in file a.h, you are including b.h. But in latter comments you say: b includes a.Jadejaded
P
3

It's a common technique to include a project level compatibility header (say compat.h) as the first header of any .c/.cpp source files, which defines a bunch of required macros like __STDC_LIMIT_MACROS, __REENTRANT and other project wide macros to affect the subsequent behavior of standard headers.

I first saw this usage long time ago by a competent programmer for an internal library. Later I saw 'git' (the infamous dvcs) project use this technique as well.

Pelayo answered 18/12, 2008 at 8:3 Comment(3)
I agree that this type of configuration-setting might be one area where a requirement for needing a header to be included in a particular order makes some sense. Another time is when you're using precompiled headers (but I'm no big fan of precompiled headers).Unripe
I've done that before, but at this point I really prefer those macros to be defined by my build args (g++ -D__STDC_LIMIT_MACROS ...) than to be part of the source themselves. To each his own, though.Bigod
Well there is no clean way to express some of the conditions (platform, compiler) in build args (which makes the build system much uglier)Pelayo
C
2

If the functions and/or classes contained in header (say, A.h) depend on functions and/or classes defined in another header (say, B.h), I prefer to include the latter in the first, rather to force the users of the first one to include both in a particular order.

Yes:

// A.h
#pragma once
// or the #ifndef trick
#include "B.h"

// A.cpp
#include "A.h"

No:

// A.h
#pragma once
// or the #ifndef trick
//#include "B.h"

// A.cpp
#include "B.h"
#include "A.h"
Conn answered 17/12, 2008 at 19:5 Comment(0)
W
1

I like including headers in alphabetical order - makes it easy to see what I've already done.

If a lib won't work because it's in the wrong order, then it's broken and should be fixed so as to be order-independent.

Wystand answered 17/12, 2008 at 20:15 Comment(0)
S
1

To me it is a bad design that, unluckily, happens to be in win32 API with socket/socket2 includes, if I recall correctly. The result is that an error in inclusion order will trigger a set of errors that just happen to come from nowhere and may be hard to debug in the cases where the dependency changes the definitions but the code still compiles.

In any other case, you will still run into trouble. If you don't include header x.h because y.h already includes it, then your code is dependent on y.h dependency on x.h. If at a later time y.h is refactored and it no longer requires y.h, the removal of the include will break your code base. This is a sign of coupling (even if not at the class level): changes in one part of the code base need to propagate and extend to other parts of code.

Schizophyceous answered 18/12, 2008 at 0:3 Comment(1)
windows.h and gl.h have the same problem, sadlyHeadrail
V
1

It's perhaps a sign that you're using MFC, which might in turn indicate bad design (joke... or is it?)

(At least, the last time I looked at MFC, it was really picky about where you included <windows.h>)

Velleman answered 18/12, 2008 at 0:17 Comment(0)
A
0

Not to my knowledge. It's pretty bad practice. I've run into it recently with a Windows header and some weird interface in my code, though.

Alard answered 17/12, 2008 at 23:22 Comment(0)
H
0

You should be using include guards and forward declarations, in that way you shouldn't have much problems with the order of including headers.

Sometimes it's still required that a header is included first or last, no idea why.
(For example: In the Source SDK)

Helse answered 18/12, 2008 at 0:7 Comment(0)
T
0

Yes, requiring a certain order for includes in c++ is a sign of bad library/header design.

Though forward declarations may require more than one file to be included in order to fully use a class. See example below:

// A.h

class B; // forward declaration

class A
{
    void doStuff(const B& b);
};

// main.cpp

#include <A.h>
#include <B.h>

int main()
{
    A a;
    B b;
    a.doStuff(b);
}
Templas answered 23/12, 2008 at 18:6 Comment(1)
This is fine, because the order of the includes in main.cpp can be reversed without a problem. It would be a problem if A.h didn't use a forward decl and didn't include B.h That would require main.cpp to include B.h before including A.hGuileless

© 2022 - 2024 — McMap. All rights reserved.