GotW #7: Minimal #includes
Difficulty: 7 / 10
[Warning: It's tougher than it looks. The comments are important.]
Most programmers #include much more than necessary. This can
seriously degrade build times, especially when a popular header
file includes too many other headers.
In the following header file, what #include directives could be
immediately removed without ill effect? Second, what further
#includes could be removed given suitable changes, and how?
(You may not change the public interfaces of classes X and Y;
that is, any changes you make to this header must not affect
current client code).
// gotw007.h (implementation file is gotw007.cpp)
//
#include "a.h" // class A
#include "b.h" // class B
#include "c.h" // class C
#include "d.h" // class D
// (note: only A and C have virtual functions)
#include <iostream>
#include <ostream>
#include <sstream>
#include <list>
#include <string>
class X : public A {
public:
X ( const C& );
D Function1( int, char* );
D Function1( int, C );
B& Function2( B );
void Function3( std::wostringstream& );
std::ostream& print( std::ostream& ) const;
private:
std::string name_;
std::list<C> clist_;
D d_;
};
std::ostream& operator<<( std::ostream& os, const X& x )
{ return x.print(os); }
class Y : private B {
public:
C Function4( A );
private:
std::list<std::wostringstream*> alist_;
};
---
Herb Sutter (mailto:he...@cntc.com)
Current Network Technologies Corp. (http://www.cntc.com)
2695 North Sheridan Way, Suite 150, Mississauga ON Canada L5K 2N6
Tel 416-805-9088 Fax 905-822-3824
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
With regard to #include'ing more than necessary: *really*? On the
contrary, my experience has been that most programmers don't bother to
include many standard headers or make assumptions that header x will
be included as a result of #include'ing header y. This makes my life
a nightmare when porting simulation code written on a Unix box over to
the Win32 machines I use. I have a special hatred for "errno".
My general guideline is that if you use a feature or class, #include
the header file that implements it -- even if (you think) it has
already been #include'd. The #ifndef guard in the header will catch
it.
If you refer to a class without actually using it, then there should
be a separate header that alludes to the class without actually
defining it. A lightweight header, if you will.
Besides, if compile times were so seriously degraded, why didn't the
C++ committee make single-inclusion a standard feature rather than the
"#pragma once" and "#ifdef/#define/#endif" guard hacks that we end up
using?
Finally, anyone who programs for the Windows platform probably finds
the exercise in optimizing build times by removing #includes a futile
one. The "#include <windows>" statement eats up the bulk of the header
compile time, anyway, precompiled headers or no. Trying to get around
it by not including <windows> and only the sub-headers usually results
in numerous compile or link errors. [Of course, I realize that most
other environments are more reasonable than Win in this regard; I
guess this is more of a rant on my part :-) ]
--
David A. Cuthbert (henry.ece.cmu.edu!dacut)
Graduate Student, Electrical and Computer Engineering
Data Storage Systems Center, Carnegie Mellon University
In general, removing the #includes requires careful thought that the
declarations being fwd declared don't change (maybe introduce new
headers like <iosfwd> that just fwd declare those classes). Also have to
be careful with typedefs (like ostream).
> // gotw007.h (implementation file is gotw007.cpp)
> //
> #include "a.h" // class A
Required.
> #include "b.h" // class B
Required. Could be avoided by restructuring the code, and declaring
some fwd declarations. (see below)
> #include "c.h" // class C
Required. Could be avoided by fwd declaring and minor change in the way
the program handles C objects.
> #include "d.h" // class D
Required. But as with C, can be easily done with fwd decl.
> // (note: only A and C have virtual functions)
> #include <iostream>
> #include <ostream>
> #include <sstream>
Here, I'd replace these with <iosfwd>.
> #include <list>
Could be removed, if the implementation was also changed.
> #include <string>
Similarly here.
> class X : public A {
This makes "a.h" necessary. This can't be avoided because A might have
virtual functions that are overridden here (in fact, A might be abstract).
> public:
> X ( const C& );
> D Function1( int, char* );
> D Function1( int, C );
These require a forward declaration for D and C. [Although I would
suspect the function taking C, since there can be derived classes, and
even the constructor may take a valid derived class of C - this is a bad
inconsistency in the interface - would change it to const C& instead to
avoid slicing]
> B& Function2( B );
These require a forward declaration for B.
> void Function3( std::wostringstream& );
> std::ostream& print( std::ostream& ) const;
This requires the forward declaration of std::wostringstream and
std::ostream.
> private:
> std::string name_;
This requires the <string> header. This could be removed by using a
pointer to the string and allocating the space from the heap.
> std::list<C> clist_;
This requires the <list> header, and the instantiation requires
"c.h". Again, you don't need that either if you use pointer to the list.
If you aren't prepared to allocate the list on the heap, you could use a
list of pointers to C, which is anyway better because there could be
derived classes for C.
> D d_;
This requires the "d.h" header. Again, no need for this if pointer is
used, could be just fwd declared.
> };
> std::ostream& operator<<( std::ostream& os, const X& x )
> { return x.print(os); }
This requires fwd decl of std::ostream.
> class Y : private B {
There is a problem with this: Why is this class in the same
file as the class X? By separating them, you could perhaps avoid some
recompilation even if you couldn't avoid #including some of the files.
[ok, this could change the way clients use it - have to #include a
different file....]
The derivation requires "b.h". This could be removed by allocating the B
object from heap and storing a pointer to it into Y objects. Since B
doesn't have virtual functions, there's no issue of overridden
functions.
> public:
> C Function4( A );
again, fwd decl for C and A.
> private:
> std::list<std::wostringstream*> alist_;
This requires the fwd declaration of std::wostringstream, and
<list>. Again, storing a pointer to the list avoids #including <list>.
(But of course, would need a fwd decl)
> };
--
Esa Pulkkinen | C++ programmers do it virtually
E-Mail: es...@cs.tut.fi | everywhere with class, resulting
WWW : http://www.cs.tut.fi/~esap/ | in multiple inheritance.
> Herb Sutter <he...@cntc.com> wrote:
>> GotW #7: Minimal #includes
>>
>> Most programmers #include much more than necessary. This can
>> seriously degrade build times, especially when a popular header
>> file includes too many other headers.
> With regard to #include'ing more than necessary: *really*? On the
> contrary, my experience has been that most programmers don't bother to
> include many standard headers or make assumptions that header x will
> be included as a result of #include'ing header y. This makes my life
> a nightmare when porting simulation code written on a Unix box over to
> the Win32 machines I use. I have a special hatred for "errno".
I think the point is not exactly about a single file being included
several times in a single compilation unit. Build time can be
increased if *every one* of 2,000 C++ source files #include's
"config.h", and "config.h" #include's (directly or indirectly) all
2,000 corresponding include-files. You'd get a noticeable improvement
in build time if you only forward-declared classes whose definition
were not necessary, and factored out the #include's into different
include-files.
--
Alexandre Oliva
mailto:ol...@dcc.unicamp.br mailto:aol...@acm.org
Universidade Estadual de Campinas, SP, Brasil
> Besides, if compile times were so seriously degraded, why didn't the
> C++ committee make single-inclusion a standard feature rather than the
> "#pragma once" and "#ifdef/#define/#endif" guard hacks that we end up
> using?
Such a feature would not really improve compile times, since
preprocessors handle stuff inside guards efficiently already. The
purpose of the exercise is to replace this:
class A {
public:
// ...
private:
// ...
};
with this:
class A;
Now this kind of change, when feasible has a far greater impact on
compile time than a single-inclusion feature would.
> Finally, anyone who programs for the Windows platform probably finds
> the exercise in optimizing build times by removing #includes a futile
> one. The "#include <windows>" statement eats up the bulk of the header
> compile time, anyway, precompiled headers or no. Trying to get around
> it by not including <windows> and only the sub-headers usually results
> in numerous compile or link errors. [Of course, I realize that most
> other environments are more reasonable than Win in this regard; I
> guess this is more of a rant on my part :-) ]
Which is why the windows.h header has a lean & mean feature (I can't
remember the name of the macro you #define), which reduces the amount
of code that get #included to a small core that is commonly used.
Other stuff can still be #included manually when it is required. You
are right though, Windows is pretty ugly in this respect.
--
______________________________________________________________________
Marcelo Cantos, Research Assistant __/_ mar...@mds.rmit.edu.au
Multimedia Database Systems Group, RMIT / _ Tel 61-3-9282-2497
723 Swanston St, Carlton VIC 3053 Aus/ralia ><_> Fax 61-3-9282-2490
Acknowledgements: errors - me; wisdom - God; funding - RMIT
> he...@cntc.com (Herb Sutter) writes:
>> #include "c.h" // class C
> Required. Could be avoided by fwd declaring and minor change in the way
> the program handles C objects.
I'd definitely replace this with `class C;'
The definition of C is not needed for the declaration of class X.
However, for any instance of X to be destructed or copy-constructed
(so that the default destructor or copy-constructor has to be
instantiated), a definition of C must have been provided, as this
would cause the implicit instantiation of the destructor or
copy-constructor of std::list<C>, which would require a definition for
the destructor or copy-constructor of C.
However, as the only constructor of X (besides the implicitly-declared
copy-constructor) takes a reference to C, I'd assume that C would be
defined whenever an instance of X would be created and/or destructed.
Obviously, the C++ source file that implemented the methods of X would
have to #include "c.h", but that's another issue.
A simple workaround for this only problem would be to explicitly
declare a copy-constructor and a destructor for X (and Y), so that
they would not be implicitly defined. Obviously, this would not
change the interface of any of these classes.
There might be a problem if the instantiation of the class body of
std::list<C> required class C to be completely defined. I could find
no such requirement in the CD2, and I wouldn't expect it to be
necessary, anyway.
>> #include "d.h" // class D
> Required. But as with C, can be easily done with fwd decl.
As a non-static class member of type D is to be declared, D must be a
completely-defined type. No fwd decl would solve this problem.
>> class X : public A {
> This makes "a.h" necessary. This can't be avoided because A might have
> virtual functions that are overridden here (in fact, A might be abstract).
The statement is true, but the actual reason is that base classes must
be completely defined.
>> public:
>> X ( const C& );
>> D Function1( int, char* );
>> D Function1( int, C );
> These require a forward declaration for D and C. [Although I would
> suspect the function taking C, since there can be derived classes, and
> even the constructor may take a valid derived class of C - this is a bad
> inconsistency in the interface - would change it to const C& instead to
> avoid slicing]
I'd rather not change it, as the problem statement says the interface
must not be changed at all. There's nothing wrong in getting a C by
value, if you're going to create a copy of it anyway, for instance.
>> private:
>> std::string name_;
> This requires the <string> header. This could be removed by using a
> pointer to the string and allocating the space from the heap.
Simple pointers usually break exception-safety. Smart pointers, on
the other hand, may require a complete type definition in order to
preserve exception safety. Allocation from heap can also decrease
performance.
--
Alexandre Oliva
mailto:ol...@dcc.unicamp.br mailto:aol...@acm.org
Universidade Estadual de Campinas, SP, Brasil
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
> With regard to #include'ing more than necessary: *really*? On the
> contrary, my experience has been that most programmers don't bother to
> include many standard headers or make assumptions that header x will
> be included as a result of #include'ing header y. This makes my life
> a nightmare when porting simulation code written on a Unix box over to
> the Win32 machines I use. I have a special hatred for "errno".
>
> My general guideline is that if you use a feature or class, #include
> the header file that implements it -- even if (you think) it has
> already been #include'd. The #ifndef guard in the header will catch
> it.
>
> If you refer to a class without actually using it, then there should
> be a separate header that alludes to the class without actually
> defining it. A lightweight header, if you will.
>
> Besides, if compile times were so seriously degraded, why didn't the
> C++ committee make single-inclusion a standard feature rather than the
> "#pragma once" and "#ifdef/#define/#endif" guard hacks that we end up
> using?
>
> Finally, anyone who programs for the Windows platform probably finds
> the exercise in optimizing build times by removing #includes a futile
> one. The "#include <windows>" statement eats up the bulk of the header
> compile time, anyway, precompiled headers or no. Trying to get around
> it by not including <windows> and only the sub-headers usually results
> in numerous compile or link errors. [Of course, I realize that most
> other environments are more reasonable than Win in this regard; I
> guess this is more of a rant on my part :-) ]
The Borland compiler (and perhaps others) caches the compiler's symbol
table in a file, but only reuses it if the next module it compiles uses
the same set of include files (and the same set of controlling
#defines). It is therefore often faster just to use the same full set of
includes for all modules in a project, even if many modules don't need
some of them. This is best done by creating one master header with
subsidiary includes, and including it and it alone.
--
Ciao,
Paul D. DeRocco
Even with #ifndef...#endif guards, the compiler still has to wade
through the entire header file to find the matching #endif;
considering the size of some of the stl headers, that's a lotta
wading!
It occurred to me that compile time might be shortened considerably
with a little extra work up front, namely:
- to move all the standard include files into a subdirectory, for
example c:\bc5\include\realheader
- to create "shell" include files in the standard directory, in this
case c:\bc5\include, with conditional guards, such as:
// shell file for iostream.h
#ifndef MY_IOSTREAM_GUARD
#define MY_IOSTREAM_GUARD
#include <realheader/iostream.h>
#endif
It wouldn't be too hard to write a small batch file (two lines) and
header creation utility (less than 50 lines of C++, I would guess)
which would move the file to the subdirectory, then create the shell
file.
Using this technique, the second and subsequent time a header was
#included, the compiler would only have to parse 4 lines (5 if you
really put in the comment). I never got a chance to try my idea to
see if it would help speed up compiles; anyone have any thoughts on
this?
[A number of compilers recognize the guard idiom, and don't even
open the file the second time around, much less wade through its
lines. -- jak ]
--
Jim Hyslop
jim.h...@leitch.com
"If you want to see the rainbow, you
gotta put up with the rain."
-- Dolly Parton
Client code could depend on specific numerical values of sizeof(X)
and sizeof(Y). Just as for <iostream> above, client code could also
depend (in a misguided attempt at removing redundant includes ;-) )
on <list> and <string> being included in this file. Client code could
depend of side effects of copy ctors of B, C or A in calls to
X::Function1(int,C), X::Function2(B), or Y::Function4(A) (I've seen
worse!). For those reasons and probably others it's extremely difficult
to change this file...
now, assuming well behaved clients, take two hypotheses : we're
allowed to split this file as "x.h" and "y.h" (SPLIT CASE below),
or not.
>
> class X : public A {
> public:
> X ( const C& );
> D Function1( int, char* );
> D Function1( int, C );
pass C by const reference (if a writeable copy is needed do it in the
body). SPLIT CASE: remove "c.h" from "x.h" and forward declare C.
> B& Function2( B );
unless serious perfromance hit, pass B by const ref and replace "b.h"
by a forward decl.
> void Function3( std::wostringstream& );
> std::ostream& print( std::ostream& ) const;
if those are virtual members of A remove <ostream> and <sstream>
> private:
> std::string name_;
> std::list<C> clist_;
> D d_;
delegate:
struct Impl;
Impl * impl_;
and wipe out <list> and <string>.
> };
> std::ostream& operator<<( std::ostream& os, const X& x )
> { return x.print(os); }
[off topic] if print is a virtual method of A then operator<< has
probably been defined for A and this is redundant; otherwise it is
vicious (should be virtual).
>
> class Y : private B {
> public:
> C Function4( A );
> private:
> std::list<std::wostringstream*> alist_;
> };
>
I'd like to see a real world example of this class! anyway,
#include "c.h" // SPLIT CASE only
class A; // SPLIT CASE only
class Y {
public:
Y();
C Function4(A const &);
private:
struct Impl;
Impl * impl_;
};
[off topic] since C has virtual functions returning a C by value
is dubious. returning an auto_ptr<C> might be more appropriate
(in which case include <memory>, remove "c.h" and forward declare
C above).
Whoa, wait a minute! I may be a c++ novice here, but if you do that,
then when you have:
class A;
class B {
private:
A myObjectA;
// ...
}
Then how is the compiler supposed to know how much space to allocate for
the storage of the A object inside an object of class B?!
:)
Jim
Jim Mitchell <jmi...@clark.net> writes:
> Whoa, wait a minute! I may be a c++ novice here, but if you do that,
> then when you have:
>
> class A;
>
> class B {
> private:
> A myObjectA;
> // ...
> }
>
> Then how is the compiler supposed to know how much space to allocate for
> the storage of the A object inside an object of class B?!
In some cases you really don't need to know the size and internal
structure of a class, especially if you use it via reference or
pointers:
class A;
class B {
// ...
A* aMethod();
// ...
};
Ciao,
struppi
--
Stefan H. Rupp Tel.: +49 241 80-5295
(dienstl.)
Geodaetisches Institut der RWTH Aachen +49 241 25209
(privat)
Templergraben 55, D-52062 Aachen, Germany Email: str...@acm.org
What could be realy great, is to be able to do a macro that would
automatically do this:
#define _INCLUDE(MyClass) #ifndef __MyClass__ \
#define __MyClass__ \
#include "Myclass.h" \
#endif
But I think that in a macro we can't use precompiler commands (right?)
Just an idea.... ;-)
Eric
--
Eric Chamberland Eric.Cha...@giref.ulaval.ca
3904 Pavillon Pouliot, GIREF Tel.: (418) 656-2131 x6274
Universite Laval, Canada, G1K 7P4 Fax : (418) 656-3404
--
Jim Hyslop
jim.h...@leitch.com
"If you want to see the rainbow, you
gotta put up with the rain."
-- Dolly Parton
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
Yes, really. That doesn't mean you never run into the problems you're
discussing though.
The problem is best described IMHO as "#include'ing a poorly thought
out set of headers".
> My general guideline is that if you use a feature or class, #include
> the header file that implements it -- even if (you think) it has
> already been #include'd. The #ifndef guard in the header will catch
> it.
>
> If you refer to a class without actually using it, then there should
> be a separate header that alludes to the class without actually
> defining it. A lightweight header, if you will.
>
> Besides, if compile times were so seriously degraded, why didn't the
> C++ committee make single-inclusion a standard feature rather than the
> "#pragma once" and "#ifdef/#define/#endif" guard hacks that we end up
> using?
On why the C++ committee didn't do something on this, I'd guess they
had more pressing concerns. That doesn't mean the problem is
insignificant.
{ And a good implementation can elide a redundant include, without
opening/parsing the header a second time, as long as the header is guarded.
See gcc for details -mod (ddv) }
Herb Sutter can probably address this better than I, but I don't
believe the problem he was referring to is fixed by the old
#ifndef guard on header files. Let me give an example of something
I caught one of my staff doing recently, and am currently dropping
bricks until he fixes it.
In a GUI application written in C++, we make use of an existing
third party OO library, and also defined about 30 higher level classes
(encapsulating the smarts of tailored dialogue boxes and
other nice things). The third party library includes a large
number of header files, basically because there are lots of
classes that it provides.
What was done?
A globals.h was written that
(1) #include'd *every* header from the third party library.
(2) #include'd *every* header file for every one of our higher
level GUI classes
(3) #include'd *every* header file for a separate class library
that handles the number crunching behind the scenes.
This globals.h is included by every .cc file written for
the GUI. Every .h file is protected by a #ifndef guard and
we're using gcc, a compiler that "optimises" the way it
handles files protected that way by not including them twice.
Let's look at what happened.
The source code for the number crunching kernel is about
1.5 Meg total. The code (including header files) for
the higher level GUI code is about 2.5 Meg.
The kernel is written using the philosophy of "include
what is needed and no more".
Compiling the entire kernel from source code (100 .cc
files) takes, on our system about 30 minutes.
Compiling each .cc file of the GUI code takes about
3-5 minutes of CPU time --- and the code in those files is not
that complex. Multiply that by 30 files, and we have about 2
hours of total compilation. Taking out the #include "globals.h"
and replacing it with 5 lines for required header files in
one of the simpler .cc files knocked compilation time
down from 3.5 minutes to just over 20 seconds. That's a fairly
significant improvement in compilation time, IMHO.
Even on the small project I'm talking about here, the cost
of the bulk inclusion was significant.
>
> Finally, anyone who programs for the Windows platform probably finds
> the exercise in optimizing build times by removing #includes a futile
> one. The "#include <windows>" statement eats up the bulk of the header
> compile time, anyway, precompiled headers or no. Trying to get around
> it by not including <windows> and only the sub-headers usually results
> in numerous compile or link errors. [Of course, I realize that most
> other environments are more reasonable than Win in this regard; I
> guess this is more of a rant on my part :-) ]
This is true of any system .... after all, the way we found out which
header files needed to be #include'd instead of globals.h was
by using compiler errors related to undeclared types, etc [I'm
surprised on a C++ compiler you could get to the linking stage
BTW]. A better way it to look at the docs ..... assuming they
exist.
The question to ask is "does the saving in effort achieved by doing
a wholesale #include of everything justify the time the programmer
spends twiddling thumbs waiting for it to compile and link?". In
even moderate sized projects, I believe the answer is "No!".
- <Automagically appended trailer>
Robert O'Dowd Phone : +61 9 553 3618
DSTO Bldg A51, Fax : +61 9 553 3577
HMAS Stirling E-mail : Robert...@dsto.defence.gov.au
Rockingham, Western Australia, 6958
Disclaimer: Opinions above are MINE and may be worth what you paid for
them.
Even with this, however, I've yet to see a precompiled header used for
another module in any substantial project. It will be used, of
course, for subsequent builds (quite nicely, in my experience). So
I'm not very convinced that using a single, large header is a good
idea.
Even worse... either I have a misconfiguration, or the latest Borland
compiler (5.2) is very broken with regard to precompiled headers -- it
attempts at times to precompile *code* from the .cpp module and is
completely braindead when it comes to the stop precompiled headers
directive (#pragma hdrstop).
Adding up all these headaches -- I have to vote for a lot of little
headers (with as few #ifdef/#endif conditional compilation options as
possible for maximum reuse) rather than the single, large header idea.
(Actually, it's tempted me to port my code over to a Unix box... until
I realized today that I don't have access to a compiler there with
namespaces, which I've been using extensively. *<big sigh>*)
--
David A. Cuthbert (henry.ece.cmu.edu!dacut)
Graduate Student, Electrical and Computer Engineering
Data Storage Systems Center, Carnegie Mellon University
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
Whoa, wait a minute! I may be a c++ novice here, but if you do that,
then when you have:
class A;
class B {
private:
A myObjectA;
// ...
}
Then how is the compiler supposed to know how much space to allocate for
the storage of the A object inside an object of class B?!
:)
Jim
This (impl_) is a good approach for insulation, and will be used in the posted
solution. However, is there any reason you continue #include "c.h" anyway?
---
Herb Sutter (mailto:he...@cntc.com)
Current Network Technologies Corp. (http://www.cntc.com)
2695 North Sheridan Way, Suite 150, Mississauga ON Canada L5K 2N6
Tel 416-805-9088 Fax 905-822-3824
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
I was unclear on my intent with this statement. I didn't mean that
such a mechanism should have been included in C++. Instead, my intent
was to say, "Hey, is #include'ing too much really a problem if the
committee didn't address it?" Obviously, looking at the e-mail I've
received, I wasn't very clear on this. :-\
On the flip side, I've noticed that some of the headers that came with
the latest version of my compiler are too complicated to allow the
compiler (Borland 5.2, at least) to do this sort of checking.
<string> is especially problematic -- it has an
#ifdef/#ifdef/#define/#endif/#else/#ifdef/#endif/#include<a .cc file>/
#endif structure. No surprise that this is too complicated; I can't
even say it ten times fast. :-)
I suspect that this sort of problem will peak between now and a few
months after C++ is finalized, and then subside as better
implementations (and header files!) come along. Backwards
compatability (which is what that huge structure is for) can be a real
pain at times...
Having said that, the C/C++ module system is, IMHO, seriously broken.
Practically nonexistent, in fact. Fixing it is impossible if
backwards compatability is even a remote issue. (At the time C was
created, though, the textual #include method was probably the only
practical and efficient method.)
>A globals.h was written that
>(1) #include'd *every* header from the third party library.
>(2) #include'd *every* header file for every one of our higher
> level GUI classes
>(3) #include'd *every* header file for a separate class library
> that handles the number crunching behind the scenes.
> [...result of optimizing the includes reducing compile time per .cc
> file to 20 sec from 3.5 min...]
Very interesting. It seems (from your reply and others that I've
heard) that my experience with working with code is very different
from the norm.
Admittedly, most of the stuff I see is written in academia by non-
software engineers, debugged just enough to get it running on the
single platform that is in use for that research group, and then
released as a courtesy to others. For these groups, writing code is
not their mainstay; in fact, it could, at times, be classified as an
annoyance (this is certainly true in my research group).
>> The "#include <windows>" statement eats up the bulk of the header
>> compile time, anyway, precompiled headers or no. Trying to get around
>> it by not including <windows> and only the sub-headers usually results
>> in numerous compile or link errors.
>This is true of any system .... after all, the way we found out which
>header files needed to be #include'd instead of globals.h was
>by using compiler errors related to undeclared types, etc [I'm
>surprised on a C++ compiler you could get to the linking stage
>BTW]. A better way it to look at the docs ..... assuming they
>exist.
Ha! I had to dig through the headers just to find the all magical
WIN32_LEAN_AND_MEAN #define. :-(
The reason why I could get it to the linking stage is because I
managed to get all the types defined correctly. It looks like some
kind of library wasn't getting included at link time; I suspect some
compiler magic going on here (but haven't mustered up enough
willingness to go through the megs of header files). It's also
possible that I was getting a false impression of type safety; my
impression of the headers I saw was: "When in doubt, just throw in a
void *." <grumble>
>The question to ask is "does the saving in effort achieved by doing
>a wholesale #include of everything justify the time the programmer
>spends twiddling thumbs waiting for it to compile and link?". In
>even moderate sized projects, I believe the answer is "No!".
For a single moderate-sized project, sure. But I write so many small
projects that it's almost worth going through and figuring out what is
what.
--
David A. Cuthbert (henry.ece.cmu.edu!dacut)
Graduate Student, Electrical and Computer Engineering
Data Storage Systems Center, Carnegie Mellon University
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
|> Jim Hyslop wrote:
|> .....
|> > // shell file for iostream.h
|> > #ifndef MY_IOSTREAM_GUARD
|> > #define MY_IOSTREAM_GUARD
|> > #include <realheader/iostream.h>
|> > #endif
|> > It wouldn't be too hard to write a small batch file (two lines) and
|> > header creation utility (less than 50 lines of C++, I would guess)
|> > which would move the file to the subdirectory, then create the shell
|> > file.
|> >
|> > Using this technique, the second and subsequent time a header was
|> > #included, the compiler would only have to parse 4 lines (5 if you
|> > really put in the comment). I never got a chance to try my idea to
|> > see if it would help speed up compiles; anyone have any thoughts on
|> > this?
I've used it on previous projects. It's a pain, but on a heavily loaded
network, we ended up cutting compile times by a factor of 3 or 4.
This is very much a factor of your environment; I've talked with people
from Sun about this, and their measurements showed at most a 10-15%
improvement. I can only conclude that they did their measurements on a
lightly loaded network or using local disk. (In the case where we
attained the factor of 3 or 4, one could very well argue that the
Ethernet was not designed for the loads we were putting on it.)
|> What could be realy great, is to be able to do a macro that would
|> automatically do this:
|>
|> #define _INCLUDE(MyClass) #ifndef __MyClass__ \
|> #define __MyClass__ \
|> #include "Myclass.h" \
|> #endif
|>
|> But I think that in a macro we can't use precompiler commands (right?)
Correct. The results of a macro expansion are not interpreted as a
preprocessor command, even if they look like one.
Concerning the original suggestion of putting the guard in a "shell
file": the measures we made on the slow network suggested that the real
cost was opening the file, not reading. So at least in this
environment, this solution wouldn't help much.
--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --
|> > Besides, if compile times were so seriously degraded, why didn't the
|> > C++ committee make single-inclusion a standard feature rather than the
|> > "#pragma once" and "#ifdef/#define/#endif" guard hacks that we end up
|> > using?
|>
|> On why the C++ committee didn't do something on this, I'd guess they
|> had more pressing concerns. That doesn't mean the problem is
|> insignificant.
Probably for the same reasons they don't mandate that a 100 line program
compile in less that 2 hours (or whatever). They technique is well
known, and used on many compilers (not just g++, although I believe that
they were the first, or one of the first). It is 100% legal and
conforming. Given that, it is a quality of implementation issue, and
nothing else.
Just out of curiousity, though: how would you specify such a
requirement. Given that, of course, an implementation is not even
required to use source files in the classical sense.
Several PC C++ implementations would be _faster_ using the bulk
includes.
This is because several PC compilers support precompiled headers.
That way you only compile the big include once, _provided_ all the .cpp
files are configured with it above any class-specific includes.
Personally I've always preferred a combination of forward declarations,
one class per header file, and isolating anything that included
windows.h!
I've worked on projects where the guard for each header was put around
the #include statement in each cpp file, so avoiding the overhead of
opening the small include file in the solution above.
I know that Lakos recommends this, but personally I'm against it. One
aesthetic worry is that now you're exposing implementation details (the header
guard name) that now appear in the client code. A more concrete worry is that
it's error-prone as well as tedious... for example, what if you mistype the
guard name in one place in the client code, where that name happens to be the
name of another (valid) macro?
So yes, I know Lakos likes it, but it sends shivers up my spine. It appears
in our coding standards in the "don't do that" section. I will admit that it
can be made to work given sufficient team discipline, however.
---
Herb Sutter (mailto:he...@cntc.com)
Current Network Technologies Corp. (http://www.cntc.com)
2695 North Sheridan Way, Suite 150, Mississauga ON Canada L5K 2N6
Tel 416-805-9088 Fax 905-822-3824
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
>Compiling each .cc file of the GUI code takes about
>3-5 minutes of CPU time --- and the code in those files is not
>that complex. Multiply that by 30 files, and we have about 2
>hours of total compilation. Taking out the #include "globals.h"
>and replacing it with 5 lines for required header files in
>one of the simpler .cc files knocked compilation time
>down from 3.5 minutes to just over 20 seconds. That's a fairly
>significant improvement in compilation time, IMHO.
Definitely significant. The solution I came up with was to use
preprocessor symbols that defined what services were needed for a
particular piece of functionality, then including a global header file
that used these preprocessor defs to include the relevant files in the
right order. No redundancy, no order problems. Here's an example:
#define USING_SOCKSTREAM
#define USING_STRINGUTILS
#define USING_FILEUTILS
#define USING_WEBSCANNER
#include "global.h"
Something like that, anyway. The USING declarations identify _services_
that the client is interested in, decoupling a service (or class family)
from the actual files that are included and also decoupling services
from _single_ files (i.e. a service might include multiple files,
without the client being concerned about such details). In this example,
the USING_WEBSCANNER symbol might include the file for URL, WebLink,
then WebDocument, then WebDocumentCollection, then WebScanner (which
uses all of the above). #defining USING_WEBDOCUMENT would only include
the first few.
The only drawback is that the "using implementation files" (I always
call them using.h, duh) can be get somewhat complex if you have a lot of
services, but you only need to edit them when the structure of your .h
files changes at all or you add a new service.
Works for me, anyway. Works pretty well, actually.
: Personally I've always preferred a combination of forward declarations,
: one class per header file, and isolating anything that included
: windows.h!
Especially since the Win32 windows.h defines a lot of macros that
can intrude into _every_ namespace (one macro for every function
that has an ANSI and Unicode version).
Are there any other platforms where this is a serious problem?
--
Tony Cook - to...@online.tmx.com.au
10023...@compuserve.com
Hmm... I don't really like this idea. Consider:
base.h:
#define USING_SOMESTUFF
#include "global.h"
class Base { ... };
derived.h:
#include "base.h"
#define USING_MORESTUFF
#include "global.h"
class Derived { ... };
You've just #included "global.h" twice, but its functionality has
changed because of the macros that have been defined. Goodbye
precompiled header.
Ok, so just make sure you #define all your macros before #include'ing
anything, right? Well, it's not so easy...:
base.h:
#define USING_SOMESTUFF
#include "module1.h"
#include "module2.h"
#include "global.h"
module1.h:
#define USING_LOTSOFSTUFF
#include "global.h"
module2.h:
#define USING_ALMOSTNOSTUFF
#include "global.h"
You have to make sure that you #define USING_ALMOSTNOSTUFF in base.h,
even though you're not using any of the functionality there, or lose
the precompiled header and any #ifdef #endif guards in global.h.
--
David A. Cuthbert (henry.ece.cmu.edu!dacut)
Graduate Student, Electrical and Computer Engineering
Data Storage Systems Center, Carnegie Mellon University
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
>Herb Sutter wrote:
>>
>> Christian Millour <chri...@club-internet.fr> wrote:
>> >#include "c.h" // SPLIT CASE only
>> >class A; // SPLIT CASE only
>> >class Y {
>> >public:
>> > Y();
>> > C Function4(A const &);
>> >private:
>> > struct Impl;
>> > Impl * impl_;
>> >};
>>
>> This (impl_) is a good approach for insulation, and will be used in the posted
>> solution. However, is there any reason you continue #include "c.h" anyway?
>>
>I was mistakenly assuming that a type returned by value needed a
>definition. The fact that it doesn't is not heavily publicised.
^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
(declaration?) (see below)
If "c.h" is not included, how does the user of Y::Function4 know what type
to expect? How, for example, can it set aside enough memory for the function
to return the C value?
>Thanks for setting me right on this one. Still learning !
I guess I am, too.
Ziv Caspi.
[...]
> On the flip side, I've noticed that some of the headers that came with
> the latest version of my compiler are too complicated to allow the
> compiler (Borland 5.2, at least) to do this sort of checking.
> <string> is especially problematic -- it has an
> #ifdef/#ifdef/#define/#endif/#else/#ifdef/#endif/#include<a .cc file>/
> #endif structure. No surprise that this is too complicated; I can't
> even say it ten times fast. :-)
>
Let's put this in separate lines, with proper indentation:
#ifdef
#ifdef
#define
#endif
#else
#ifdef
#endif
#include<a .cc file>
#endif
Now we see immediatly that the outermost #ifdef has an #else part
and is therefore *not* an ifdef guard. So no compiler - regardless
how smart - will be able to optimize this. (Ok, there's one case
which a really clever preprocessor could optimize: If the first
label is defined, and the second isn't. But that goes beyond ifdef
guard optimizing, and the general case could get very complicated soon.)
It either lacks an additional ifdef guard, or is explicitly
written for multiple inclusion.
[...]
>Hmm... I don't really like this idea. Consider:
>
>base.h:
>#define USING_SOMESTUFF
>#include "global.h"
>
>class Base { ... };
Sorry. I should have specified: I don't allow *any* #includes in a .h
file other than STL headers like <map>, <set>, etc., which might change
often. All inclusions are done via the .cpp file.
|> Christian Millour <chri...@club-internet.fr> wrote:
|>
|> >Herb Sutter wrote:
|> >>
|> >> Christian Millour <chri...@club-internet.fr> wrote:
|> >> >#include "c.h" // SPLIT CASE only
|> >> >class A; // SPLIT CASE only
|> >> >class Y {
|> >> >public:
|> >> > Y();
|> >> > C Function4(A const &);
|> >> >private:
|> >> > struct Impl;
|> >> > Impl * impl_;
|> >> >};
|> >>
|> >> This (impl_) is a good approach for insulation, and will be used in the posted
|> >> solution. However, is there any reason you continue #include "c.h" anyway?
|> >>
|> >I was mistakenly assuming that a type returned by value needed a
|> >definition. The fact that it doesn't is not heavily publicised.
|> ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
|> (declaration?) (see below)
|>
|> If "c.h" is not included, how does the user of Y::Function4 know what type
|> to expect? How, for example, can it set aside enough memory for the function
|> to return the C value?
The user of Y::Function4 must include c.h. This is normal, he uses the
definitions in the header file. But potentially many or most users of Y
won't use this particular function, and they don't have to include c.h.
--
James Kanze home: ka...@gabi-soft.fr +33 (0)1 39 55 85 62
office: ka...@vx.cit.alcatel.fr +33 (0)1 69 63 14 54
GABI Software, Sarl., 22 rue Jacques-Lemercier, F-78000 Versailles France
-- Conseils en informatique industrielle --
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
Isn't this a bit strict? If you write Base.h (containing class Base)
and I want to derive from it, it makes sense for Derived.h to #include
Base.h. Otherwise, anyone and everyone who uses Derived.h has to
#include Base.h -- i.e., they have to know a bit of the implementation
of my class.
--
David A. Cuthbert (henry.ece.cmu.edu!dacut)
Graduate Student, Electrical and Computer Engineering
Data Storage Systems Center, Carnegie Mellon University
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
>Steve Willer <wil...@interlog.com> wrote:
>>Sorry. I should have specified: I don't allow *any* #includes in a .h
>>file other than STL headers like <map>, <set>, etc., which might change
>>often. All inclusions are done via the .cpp file.
>
>Isn't this a bit strict? If you write Base.h (containing class Base)
>and I want to derive from it, it makes sense for Derived.h to #include
>Base.h. Otherwise, anyone and everyone who uses Derived.h has to
>#include Base.h -- i.e., they have to know a bit of the implementation
>of my class.
Well, see, this is dealt with by the USING system that I was talking
about. If you #define USING_DERIVED, then the "USING" system is supposed
to automatically include the headers for Base before Derived. Do you see
the simplicity of it from the client point of view? Granted, it can be
annoying to maintain, but the onus for inclusion order, keeping headers
under control, etc., is on the library maintainer.
Remember, this little sub-thread is on the USING thing. Not on direct
#inlcusions, which is something I've already stated I don't like.
>Let's put this in separate lines, with proper indentation:
>
>#ifdef
> #ifdef
> #define
> #endif
>#else
> #ifdef
> #endif
> #include<a .cc file>
>#endif
If you want this to work everywhere, you have to put the # at the
beginning of the line always. Then you can indent after the #. For
example:
#ifdef
# ifdef
# define
# endif
#else
# ifdef
# endif
# include <a .cc file>
#endif
Steve Willer <wil...@interlog.com> wrote [re: not allowing any #includes
>>in a .h file other than STL headers].
da...@henry.ece.cmu.edu (David A. Cuthbert) replied:
>>Isn't this a bit strict? If you write Base.h (containing class Base)
>>and I want to derive from it, it makes sense for Derived.h to #include
>>Base.h. Otherwise, anyone and everyone who uses Derived.h has to
>>#include Base.h -- i.e., they have to know a bit of the implementation
>>of my class.
Steve Willer <wil...@interlog.com> wrote:
>Well, see, this is dealt with by the USING system that I was talking
>about. If you #define USING_DERIVED, then the "USING" system is supposed
>to automatically include the headers for Base before Derived. Do you see
>the simplicity of it from the client point of view? Granted, it can be
>annoying to maintain, but the onus for inclusion order, keeping headers
>under control, etc., is on the library maintainer.
You're assuming that there's one library maintainer. Perhaps this is
true at your company currently; I don't see how to extend this scheme
to include multiple programmers.
For example, how do you handle the situation where you take a given
library (say, one that someone wrote long ago), and two separate
programmers write classes derived from a class defined in the library?
Now you have to merge in the additions -- but how? I suppose a
revision control system is useful here. (FWIW, I've never used one,
and am somewhat skeptical about what they can do as a result; yeah,
I'm a pessimist when it comes to software :-)
I don't know. I have a dislike for the C/C++ module system (or lack
thereof) which grows stronger every day, but I think the USING
#defines are a good solution only for a rather limited set of
environments.
--
David A. Cuthbert (henry.ece.cmu.edu!dacut)
Graduate Student, Electrical and Computer Engineering
Data Storage Systems Center, Carnegie Mellon University
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
>You're assuming that there's one library maintainer. Perhaps this is
>true at your company currently; I don't see how to extend this scheme
>to include multiple programmers.
The USING thing is implemented by a network of using.h files. The main
include file includes the using.h files for each of the individual
libraries. Individual library maintainers can change their individual
using.h files without impacting anyone else.
>For example, how do you handle the situation where you take a given
>library (say, one that someone wrote long ago), and two separate
>programmers write classes derived from a class defined in the library?
>Now you have to merge in the additions -- but how?
Just change one of the using.h files. No biggie.
> I suppose a
>revision control system is useful here. (FWIW, I've never used one,
>and am somewhat skeptical about what they can do as a result; yeah,
>I'm a pessimist when it comes to software :-)
Wow. Talk about shooting yourself in the foot. I would strongly
recommend you use a revision control system; like backups, it probably
won't seem important until you need it, and then you're already in a lot
of trouble.
>I don't know. I have a dislike for the C/C++ module system (or lack
>thereof) which grows stronger every day, but I think the USING
>#defines are a good solution only for a rather limited set of
>environments.
If it's an environment that's under some reasonable control, it works
quite well. If you're using a lot of external code, its utility is
somewhat lessened unless you generate USING symbols for the external
library. It's true that the mapping between USING symbols and inclusions
needs to be manually maintained, but that work is only imposed on
probably a single person, as opposed to imposing it on each and every
person who writes a module. Also, it makes it possible to change the
header files in any way you like without changing any client code --
just change the using.h files.
----
Steve Willer (ex-SoftQuad) http://www.interlog.com/~willer