By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
446,403 Members | 932 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 446,403 IT Pros & Developers. It's quick & easy.

Large scale C++ software design,

P: n/a
Large scale C++ software design, by John Lakos - people any good
recommendations to this book?

Jul 23 '05 #1
Share this Question
Share on Google+
28 Replies


P: n/a
Definitely worthwhile. /david :-)

Jul 23 '05 #2

P: n/a

davidru...@warpmail.net wrote:
Definitely worthwhile. /david :-)


Yep, I second that. :)

-shez-

Jul 23 '05 #3

P: n/a
puzzlecracker wrote:
Large scale C++ software design, by John Lakos
- people any good recommendations to this book?


This book was published in 1996
and contains a lot of obsolete advice.
For example, Chapter 2 Ground Rules,
Section 5 Redundant Include Guards, page 85,

Minor Design Rule
Place a redundant (external) include guard
around each preprocessor include directive
in every header file.

Today, we expect the C preprocessor to remember
idempotent header files and read them only once.
Jul 23 '05 #4

P: n/a

E. Robert Tisdale wrote:
puzzlecracker wrote:
Large scale C++ software design, by John Lakos
- people any good recommendations to this book?


This book was published in 1996
and contains a lot of obsolete advice.
For example, Chapter 2 Ground Rules,
Section 5 Redundant Include Guards, page 85,

Minor Design Rule
Place a redundant (external) include guard
around each preprocessor include directive
in every header file.

Today, we expect the C preprocessor to remember
idempotent header files and read them only once.

what would you supplement this book with? Any viable alternatives or
construction suggestions that would go on par with this book?

Jul 23 '05 #5

P: n/a
I would personally recommend Meyers, Dewhurst, and various books by
Sutter. However, none of these really address physical design in the
way that Lakos does.

Jul 23 '05 #6

P: n/a

E. Robert Tisdale wrote:
This book was published in 1996
and contains a lot of obsolete advice.
For example, Chapter 2 Ground Rules,
Section 5 Redundant Include Guards, page 85,

Minor Design Rule
Place a redundant (external) include guard
around each preprocessor include directive
in every header file.

Today, we expect the C preprocessor to remember
idempotent header files and read them only once.


What are you talking about? Is that specified in standard C++? Does
the following code compile for you?

blah.h
------
int somefunction() { return 9; }

blah.cpp
--------
#include <iostream>
#include "blah.h"
#include "blah.h"

int main() { std::cout << somefunction() << std::endl; }

I tried it on gpp and got "error: redefinition of 'int
somefunction()'". If it compiles for you, I'd really like to know what
compiler you're using.

Thanks,
-shez-

Jul 23 '05 #7

P: n/a
"Shezan Baig" <sh************@gmail.com> wrote...

E. Robert Tisdale wrote:
This book was published in 1996
and contains a lot of obsolete advice.
For example, Chapter 2 Ground Rules,
Section 5 Redundant Include Guards, page 85, ^^^^^^^^^
Minor Design Rule
Place a redundant (external) include guard ^^^^^^^^ around each preprocessor include directive
in every header file.

Today, we expect the C preprocessor to remember
idempotent header files and read them only once.
What are you talking about?


He is talking about advice John Lakos gave in his book.
Is that specified in standard C++?
Why should that matter? The Standard doesn't specify any
design guidelines nor does it say what features of a C++
compiler make a better competitor on today's market. If
the preprocessor remembers what headers it has already
included, the redundant guards become unnecessary.
Does
the following code compile for you?

blah.h
------
int somefunction() { return 9; }

blah.cpp
--------
#include <iostream>
#include "blah.h"
#include "blah.h"

int main() { std::cout << somefunction() << std::endl; }

I tried it on gpp and got "error: redefinition of 'int
somefunction()'". If it compiles for you, I'd really like to know what
compiler you're using.


No, what you presented here will certainly not compile.
However, if you add

#pragma once

to 'blah.h', some modern compilers will not preprocess it
again. The existence of 'once' pragma makes *external*
include guards unnecessary.

V
Jul 23 '05 #8

P: n/a
These small details are less important compared to the overall
methodology presented in the book. The main point is to recognize that
physical design is a separate problem from logical design, and then to
learn how to implement a physical design.

Jul 23 '05 #9

P: n/a
Victor Bazarov wrote:
However, if you add

#pragma once

to 'blah.h', some modern compilers will not preprocess it
again. The existence of 'once' pragma makes *external*
include guards unnecessary.


That worked! Cool, thanks!

-shez-

Jul 23 '05 #10

P: n/a
Shezan Baig wrote:
E. Robert Tisdale wrote:
This book was published in 1996
and contains a lot of obsolete advice.
For example, Chapter 2 Ground Rules,
Section 5 Redundant Include Guards, page 85,

Minor Design Rule
Place a redundant (external) include guard
around each preprocessor include directive
in every header file.

Today, we expect the C preprocessor to remember
idempotent header files and read them only once.
What are you talking about?
Is that specified in standard C++?
Does the following code compile for you?

blah.h
------
int somefunction() { return 9; }

blah.cpp
--------
#include <iostream>
#include "blah.h"
#include "blah.h"

int main() { std::cout << somefunction() << std::endl; }

I tried it on gpp
and got "error: redefinition of 'int somefunction()'".
If it compiles for you,
I'd really like to know what compiler you're using.


You are confused.
John Lakos might write:
cat blah.h #ifndef GUARD_BLAH_H // *internal* include guard
#define GUARD_BLAH_H 1
int somefunction() { return 9; }
#endif//GUARD_BLAH_H 1
cat blah.cpp

#include <iostream>
#ifndef GUARD_BLAH_H // *external* include guard
#include "blah.h"
#endif//GUARD_BLAH_H
#ifndef GUARD_BLAH_H // *external* include guard
#include "blah.h"
#endif//GUARD_BLAH_H

int main() { std::cout << somefunction() << std::endl; }

The redundant external include guards are not necessary
because, after the C preprocessor reads blah.h once,
it remembers that blah.h is idempotent
and it won't even attempt to read it a second time.
Jul 23 '05 #11

P: n/a
Victor Bazarov wrote:
No, what you presented here will certainly not compile.
However, if you add

#pragma once

to 'blah.h', some modern compilers will not preprocess it
again. The existence of 'once' pragma makes *external*
include guards unnecessary.

Still it can not be considered portable, and I wonder why at least the
2003 standard did not include this directive.


--
Ioannis Vranos

http://www23.brinkster.com/noicys
Jul 23 '05 #12

P: n/a
puzzlecracker wrote:
Large scale C++ software design, by John Lakos - people any good
recommendations to this book?


The book is somewhat aged and predates modern C++ techniques
like template meta programming which are neither covered nor
can be addressed with the techniques in this book (partially
because compiler writers refuse to implement the complete
standard, in particular exporting templates). If you keep
this in mind, Lakos' book is excellent reading.

The discussion of the [redundant] external include guards
brought up in thread may be outdata after nearly 10 more years
but it is only one of many techniques Lakos uses to keep huge
project managable. Many of the other techniques have impacts
on the actual C++ code as well as on object oriented design
for achieving independent units.
--
<mailto:di***********@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

Jul 23 '05 #13

P: n/a
In message <ct**********@nntp1.jpl.nasa.gov>, E. Robert Tisdale
<E.**************@jpl.nasa.gov> writes
John Lakos might write:
> cat blah.h

#ifndef GUARD_BLAH_H // *internal* include guard
#define GUARD_BLAH_H 1
int somefunction() { return 9; }
#endif//GUARD_BLAH_H 1
> cat blah.cpp

#include <iostream>
#ifndef GUARD_BLAH_H // *external* include guard
#include "blah.h"
#endif//GUARD_BLAH_H
#ifndef GUARD_BLAH_H // *external* include guard
#include "blah.h"
#endif//GUARD_BLAH_H

int main() { std::cout << somefunction() << std::endl; }

The redundant external include guards are not necessary
because, after the C preprocessor reads blah.h once,
it remembers that blah.h is idempotent
and it won't even attempt to read it a second time.


The C++ preprocessor has no notion of idempotency. (I have no idea
whether the C preprocessor does or does not, and it's off-topic here
anyway.) All it "remembers" is that the include-guard macro has been
defined.

If you mean "include guards should go inside the included file, not
outside", just say so.
--
Richard Herring
Jul 23 '05 #14

P: n/a
No, redundant guards go in the .h file only, not in the .cpp file. The
reason has more to do with reducing compile time than providing some
kind of protection against multiply defined symbols. /david

Jul 23 '05 #15

P: n/a
Ioannis Vranos wrote:
Victor Bazarov wrote:
No, what you presented here will certainly not compile.
However, if you add

#pragma once

to 'blah.h', some modern compilers will not preprocess it
again. The existence of 'once' pragma makes *external*
include guards unnecessary.


Still it can not be considered portable, and I wonder why at least the
2003 standard did not include this directive.


Pragmas are by definition implementation-defined.

There are two sides to the issue: following language rules and good
large-scale system design. They are orthogonal. If some compiler
features that aren't in the language proper allow you to achieve
a better result in the system design, who is to tell you not to use
it because "it can not be considered portable"? Who cares that it
is not portable if it does what it intends to do?

IOW, don't get hung up on portability.

V
Jul 23 '05 #16

P: n/a
da********@warpmail.net wrote:
No, redundant guards go in the .h file only, not in the .cpp file.


Well, then, they're not *redundant*, now, are they?

--
Mike Smith
Jul 23 '05 #17

P: n/a
Mike Smith wrote:
da********@warpmail.net wrote:
No, redundant guards go in the .h file only, not in the .cpp file.


Well, then, they're not *redundant*, now, are they?


They are: the idea is that you enclose each '#include' statement
in a header file with the same guards as the header file uses
internally. That is, if you omit the redundant guards, there will
be no semantic difference. However, the compiler does not need to
search and open the included header file if it was included before
due to the redundant guards.

For example:

/**/ // file: foo.h
/**/ #if !defined(FOO_H) // non-redundant guard
/**/ #define FOO_H
/**/ ...
/**/ #endif

/**/ // file: bar.h
/**/ #if !defined(BAR_H) // non-redundant guard
/**/ #define BAR_H
/**/ # if !defined(FOO_H) // <--- redundant guard!
/**/ # include "foo.h"
/**/ # endif
/**/ #endif

Now, is this an important technique? Probably not today: Some
compiler added a non-portable #pragma to tell the compiler about
headers which only need to be included once (you should, however,
still provide the non-redundant guards anyway to avoid portability
problems; on the other hand, adding these guards can be done
automatically quite easy). Other compilers, e.g. gcc, detect that
the header has include guards and include it once anyway which is,
BTW, one of the reasons why there is no intention whatsoever to
bless the '#pragma once' thing by the standard.

At the time of Lakos' writing the optimizations techniques were no
widespread and include hierarchies often nested quite deep. Combine
this with slower processing (the preprocessor needs to read the
whole file) and [slower] network file systems and you will see the
impact of this technique. However, he goes actually much further
and things become much more interesting: he shows how to organize
the code to make inclue statements obsolete without changing any
function or class just by taking advantage of the knowledge about
things really necessary for declarations to work. ... and he goes
beyond this to decouple classes even further making even more of
the preprocessor hacking unnecessary. The impact of the refactoring
(if you don't do it right from the start of the project) is
tremendous and goes far beyond mere compile-time (although that
really matters; around the time he released his book I worked in a
project where a single built of the system took about 16 hours):
you can change things much more freely if you decouple them
religously (actually, in the same project mentioned above, a change
to essentially an arbitrary class caused the need to recompile
everything, at least from make's perspective; guess how successful
that project was...).

If you are working on a bigger piece of software, Lakos' is still
a must-read, IMO. It may be irrelevant to average toy programs but
it is still relevant for large-scale projects! ... and, to some
extend, even to projects in a different language than C++ (which
also sheds some light on the importance of include guard thingy).
--
<mailto:di***********@yahoo.com> <http://www.dietmar-kuehl.de/>
<http://www.contendix.com> - Software Development & Consulting

Jul 23 '05 #18

P: n/a
Dietmar Kuehl wrote:
Mike Smith wrote:
da********@warpmail.net wrote:
No, redundant guards go in the .h file only, not in the .cpp file.


Well, then, they're not *redundant*, now, are they?

They are: the idea is that you enclose each '#include' statement
in a header file with the same guards as the header file uses
internally. That is, if you omit the redundant guards, there will
be no semantic difference.


And once you omit the redundant guards, then they can no longer be
*redundant*. The ones in the .cpp file are the redundant ones; once you
remove them, you're left with the ones in the .h file, which are *not*
redundant.

--
Mike Smith
Jul 23 '05 #19

P: n/a

Mike Smith wrote:
Dietmar Kuehl wrote:
Mike Smith wrote:
da********@warpmail.net wrote:

No, redundant guards go in the .h file only, not in the .cpp file.

Well, then, they're not *redundant*, now, are they?

They are: the idea is that you enclose each '#include' statement
in a header file with the same guards as the header file uses
internally. That is, if you omit the redundant guards, there will
be no semantic difference.


And once you omit the redundant guards, then they can no longer be
*redundant*. The ones in the .cpp file are the redundant ones; once

you remove them, you're left with the ones in the .h file, which are *not* redundant.


You don't put redundant guards in your .cpp files. You put them in
header files (when you include other header files).

Hope this helps,
-shez-

Jul 23 '05 #20

P: n/a
Shezan Baig wrote:
You don't put redundant guards in your .cpp files.
You put them in header files (when you include other header files).


This was a maintenance nightmare.
If you changed the name of the internal guard macro in a header file,
you had to change the name of the external guard macro to match
in every header or source file that included it.
Jul 23 '05 #21

P: n/a

E. Robert Tisdale wrote:
Shezan Baig wrote:
You don't put redundant guards in your .cpp files.
You put them in header files (when you include other header files).


This was a maintenance nightmare.
If you changed the name of the internal guard macro in a header file,
you had to change the name of the external guard macro to match
in every header or source file that included it.


Not really. Why would you change it? The name of the include guard is
derived from the name of the header file. There should be no need to
change it. Unless of course you change the name of your header file,
in which case you would *have* *to* grep for all references to it
anyway.

Hope this helps,
-shez-

Jul 23 '05 #22

P: n/a

E. Robert Tisdale wrote:
Shezan Baig wrote:
You don't put redundant guards in your .cpp files.
You put them in header files (when you include other header files).


This was a maintenance nightmare.
If you changed the name of the internal guard macro in a header file,
you had to change the name of the external guard macro to match
in every header or source file that included it.


Not really. Why would you change it? The name of the include guard is
derived from the name of the header file. There should be no need to
change it. Unless of course you change the name of your header file,
in which case you would *have* *to* grep for all references to it
anyway.

Hope this helps,
-shez-

Jul 23 '05 #23

P: n/a
Shezan Baig wrote:
E. Robert Tisdale wrote:
Shezan Baig wrote:
You don't put redundant guards in your .cpp files.
You put them in header files (when you include other header files).
This was a maintenance nightmare.
If you changed the name of the internal guard macro in a header file,
you had to change the name of the external guard macro to match
in every header or source file that included it.


Not really. Why would you change it?
The name of the include guard is derived from the name of the header file.


Where is it written that, "The name of the include guard
is derived from the name of the header file."
There should be no need to change it.
Unless of course you change the name of your header file, in which case,
you would *have* *to* grep for all references to it anyway.

Jul 23 '05 #24

P: n/a

E. Robert Tisdale wrote:
Where is it written that, "The name of the include guard
is derived from the name of the header file."


Sorry, I didn't make my point totally clear with that quote. I didn't
intend for it to mean "it *must* be derived from the header file", but
it is definitely highly recommended. See Lakos p82, paragraph 2.

Hope this helps,
-shez-

Jul 23 '05 #25

P: n/a
FYI, the technique is implemented something like this: for
everycomponent file "foo.h" the include guard is called INCLUDED_FOO.
Now, coupled with package prefixes, each component file is actually
called "my_foo.h" and "your_foo.h", and the include guards are
INCLUDED_MY_FOO and INCLUDED_YOUR_FOO. This way, all filenames, and all
include guard names are unique across the project (and hopefully the
company). /david

Jul 23 '05 #26

P: n/a
Shezan Baig wrote:
E. Robert Tisdale wrote:
Shezan Baig wrote:
You don't put redundant guards in your .cpp files.
You put them in header files (when you include other header files).


This was a maintenance nightmare.
If you changed the name of the internal guard macro in a header file,
you had to change the name of the external guard macro to match
in every header or source file that included it.


Not really. Why would you change it? The name of the include guard
is derived from the name of the header file. There should be no need
to change it. Unless of course you change the name of your header
file, in which case you would *have* *to* grep for all references to
it anyway.


One way the include guard names and the header names can get out of sync is when
a header is renamed but the internal include guards are not updated.

Jonathan

Jul 23 '05 #27

P: n/a
Victor Bazarov wrote:
Pragmas are by definition implementation-defined.

There are two sides to the issue: following language rules and good
large-scale system design. They are orthogonal. If some compiler
features that aren't in the language proper allow you to achieve
a better result in the system design, who is to tell you not to use
it because "it can not be considered portable"? Who cares that it
is not portable if it does what it intends to do?

IOW, don't get hung up on portability.

Yes, however still the standard could have adopted #pragma once as an
alternative to inclusion guards.


--
Ioannis Vranos

http://www23.brinkster.com/noicys
Jul 23 '05 #28

P: n/a
Jonathan Turkanis wrote:
One way the include guard names and the header names can get out of sync is when
a header is renamed but the internal include guards are not updated.

I do not know about the embedded world, however in the PC world most
(all?) IDEs provide the ability of "(find and) replace all" so you can
change a text found anywhere in the project with another text.


--
Ioannis Vranos

http://www23.brinkster.com/noicys
Jul 23 '05 #29

This discussion thread is closed

Replies have been disabled for this discussion.