Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

If (global header files are bad) then …?

20 views
Skip to first unread message

Ishan De Silva

unread,
Feb 16, 2004, 2:45:53 AM2/16/04
to
Hi,

[I'm not sure whether this is the right newsgroup for posting this
type of a question. If so please point me to the correct group]

Recently I read an essay by Mr. Joseph M. Newcomer (JMN) on global
header files. There he says that it is really bad programming style to
have one application-wide header file (called Defs.h or something like
that) to have all your definitions such as typedefs, enums, #define
etc.

I have practically experienced this in my recent projects (these are
VC++ 6.0 projects) involving more than 100s of classes. (I don't know
how "big" these project are compared to general acceptance in the
industry) I came across the exact issue that JMN is talking about
where, addition of a single definition required by, perhaps, a single
class results in the whole project been recompiled.

Now I have a different project where I would like to employ a more
"stylish" approach to managing definitions. Therefore, I would like to
get some advice from senior developers who have been involved in
"really large" projects.

I have the following questions.

- If a particular definition (say a typedef) defined in one header
file (class1.h) is needed by another class in class2.h, should we
simply #include class1.h in class2.h OR move the typedef to a common.h
and #include common.h in both class1.h and class2.h? (I usually have
one pair of .h and .cpp files per class)

- What is the delimitation of a "component" in a c++ project? Is it a
single pair of .h and .cpp files OR a set of .h/.cpp pairs, all of
which #include a common.h file of the nature I described above?

- What is a compilation unit? Is it same as a "translation unit"? Does
a "global header file" make your entire project a single "compilation
unit"?

- As the number of source files in a project increase, how can we
visualize and manage the header file dependencies so that we avoid
situations such as cyclic dependencies or the emergence of
nearly-global header files?

- Are there any tools that help us visualize header file dependencies?
Are these tools useful in managing the set of source files in a large
project?

Thanks in advance,
Ishan.

Mohamed Imam

unread,
Feb 16, 2004, 9:38:49 AM2/16/04
to

"Ishan De Silva" <ides...@eudoramail.com> wrote in message
news:b5f293d.04021...@posting.google.com...

> Hi,
>
> [I'm not sure whether this is the right newsgroup for posting this
> type of a question. If so please point me to the correct group]
>
> Recently I read an essay by Mr. Joseph M. Newcomer (JMN) on global
> header files. There he says that it is really bad programming style to
> have one application-wide header file (called Defs.h or something like
> that) to have all your definitions such as typedefs, enums, #define
> etc.
>
> I have practically experienced this in my recent projects (these are
> VC++ 6.0 projects) involving more than 100s of classes. (I don't know
> how "big" these project are compared to general acceptance in the
> industry) I came across the exact issue that JMN is talking about
> where, addition of a single definition required by, perhaps, a single
> class results in the whole project been recompiled.
>
> Now I have a different project where I would like to employ a more
> "stylish" approach to managing definitions. Therefore, I would like to
> get some advice from senior developers who have been involved in
> "really large" projects.
>
> I have the following questions.
>
> - If a particular definition (say a typedef) defined in one header
> file (class1.h) is needed by another class in class2.h, should we
> simply #include class1.h in class2.h OR move the typedef to a common.h
> and #include common.h in both class1.h and class2.h? (I usually have
> one pair of .h and .cpp files per class)

I think you should divide into modules where each module has it's own header
def file. In case typedefs are shared between two modules it should be
placed in a separate common file. This depends on the use case of the
classes also. If class A uses class B it will include it's def header file
implicitly.


>
> - What is the delimitation of a "component" in a c++ project? Is it a
> single pair of .h and .cpp files OR a set of .h/.cpp pairs, all of
> which #include a common.h file of the nature I described above?
>

A "component" in a c++ project can be considered as the c++ class. Which
might be a header file or/and a header/cpp file pairs. I do not understand
exactly what is the delimitation that you are meaning here.


> - What is a compilation unit? Is it same as a "translation unit"? Does
> a "global header file" make your entire project a single "compilation
> unit"?
>
> - As the number of source files in a project increase, how can we
> visualize and manage the header file dependencies so that we avoid
> situations such as cyclic dependencies or the emergence of
> nearly-global header files?
>

I think this problem can be resolved by design the code hierarchy first
before writing actual code. This will help you determine which parts depends
on others.
You can separate common definitions in header files.

There is no need to have one global header file unless you are going to
reditribute your code as a library. Same as <windows.h> includes commonly
used header files.

> - Are there any tools that help us visualize header file dependencies?
> Are these tools useful in managing the set of source files in a large
> project?
>
> Thanks in advance,
> Ishan.

I hope i have replied to some of your questions.

Thanks,
Mohamed


Doug Harrison [MVP]

unread,
Feb 16, 2004, 12:31:40 PM2/16/04
to
Ishan De Silva wrote:

>- If a particular definition (say a typedef) defined in one header
>file (class1.h) is needed by another class in class2.h, should we
>simply #include class1.h in class2.h OR move the typedef to a common.h
>and #include common.h in both class1.h and class2.h? (I usually have
>one pair of .h and .cpp files per class)

Presumably, the typedef is closely related to the other things defined in
class1.h, or else it wouldn't be there in the first place. Therefore,
class2.h should #include class1.h. On the other hand, if the typedef can be
used independently of class1.h, then it might make sense to factor it out
into another header, which itself should contain related things as much as
possible.

>- What is the delimitation of a "component" in a c++ project? Is it a
>single pair of .h and .cpp files OR a set of .h/.cpp pairs, all of
>which #include a common.h file of the nature I described above?

You'll have to define "component" before I can comment on what comprises
one. :)

>- What is a compilation unit? Is it same as a "translation unit"?

Apparently the C++ Standard considers them the same thing, though it uses
"translation unit" almost exclusively, such that the two uses of
"compilation unit" I find in it appear to be oversights. In C++, a
translation unit is a transient entity consisting of a source file after all
its #includes have been processed.

>Does a "global header file" make your entire project a single "compilation
>unit"?

Nope. If your project consists of more than one source file (compiled into
separate object files), then it has more than one translation unit. I
suppose you could say your project has as many translation units as it does
object files.

>- As the number of source files in a project increase, how can we
>visualize and manage the header file dependencies so that we avoid
>situations such as cyclic dependencies or the emergence of
>nearly-global header files?
>
>- Are there any tools that help us visualize header file dependencies?
>Are these tools useful in managing the set of source files in a large
>project?

Those are good questions. I feel certain there are such tools, but I can't
name any for you. In VC7, there is a compiler option /showIncludes which
will print the header files included directly and indirectly, so you can get
the raw data for analysis. Also, the IDE build system tracks these
dependencies, but I don't know if it exposes that information through the
IDE object model. VC6 certainly does not.

--
Doug Harrison
Microsoft MVP - Visual C++

Joseph M. Newcomer

unread,
Feb 16, 2004, 6:54:31 PM2/16/04
to
Most of these would be the answers I would have given. So let me add some more
information.

One way to avoid header file problems. It is the "self-contained header file" notion.

Every header file starts out with

#ifndef SOME_REALLY_BIG_NAME_WHICH_IS_UNIQUE_TO_THIS_HEADER_FILE
#define SOME_REALLY_BIG_NAME_WHICH_IS_UNIQUE_TO_THIS_HEADER_FILE

#pragma once
#include "a.h"
#include "b.h"

... definitions here
#endif

where a.h and b.h are both required to make this header file compile without errors. Since
a.h and b.h may be included by other header files, the #ifdef technique, known as "include
guards", keeps the file from producing multiple definitions. Note that if b.h requires c.h
to successfully compile, b.h will itself include c.h, so you don't have to care. If you
already included it, the include guards mean that it effectively disappears, and if you
haven't, but include it later, (possibly by using some other header file that includes
it), the later inclusions will be noops.

Note that classes created by VS6 get GUID-based symbols for the include guards.

It also helps if you are creating a lot of subclasses to REMOVE the ridiculous #include of
the project.h file (whatever your project is); it is almost totally irrelevevant nearly
all of the time. My policy is to either eliminate it entirely (most places) or replace it
with #include "resource.h" (for dialog classes and some control classes). If you remove it
and get undefined symbols, they are almost always symbols in resource.h. For proper
structuring, there is no sane reason that most of your classes ever need access to the
CWinApp class you have, and all this does is add an unnecessary dependency that largely
makes no sense (and if you ARE using CWinApp, rethink what you are doing. Beyond the
project file itself, and perhaps the mainfrm.cpp, you should keep away from it.

If you are VC6 and above (maybe earlier, but definitely VC6), the #pragma once optimizes
this even further; instead o even bothering to open the file, the compiler checks to see
if it has already been included and if so, skips it. VC7 doesn't bother with the include
guards at all, and relies entirely on the #pragma once (which in VC6 is case-sensitive,
although the file system is not. I don't know if they fixed this in VC7; if they didn't,
relying on #pragma once is a fatal mistake).


On Mon, 16 Feb 2004 16:38:49 +0200, "Mohamed Imam" <Mohame...@egdsc.microsoft.com>
wrote:

Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm

Ishan De Silva

unread,
Feb 16, 2004, 11:55:09 PM2/16/04
to
First of all, thanks for the answers to other questions.

>
> >- What is the delimitation of a "component" in a c++ project? Is it a
> >single pair of .h and .cpp files OR a set of .h/.cpp pairs, all of
> >which #include a common.h file of the nature I described above?
>
> You'll have to define "component" before I can comment on what comprises
> one. :)
>

This is a tough one. :-)

What I meant as a "component" is a "set of source code" that can be
re-used directly in another application.

I will use the following example to further explain my point.

Let's say we want to develop a GUI control that help us display
"tabular" data such as time tables, sales data etc., with features
such as drag and drop between table cells, in-place editing of cells
etc. Now, CListCtrl of MFC is not suitable for this. Writing such a
control would involve defining classes to represent the Cells,
Columns, Rows and finally the Table.
What I meant as a "component" is something like the above table
control.

Now, should we be defining all these classes in a pair of .h/.cpp
files (called something like Table.h and Table.cpp) OR should they be
defined in separate files?

When we go thru MFC source files, we see that one .cpp files defines a
large number of classes. Why is it done that way? Do they put all the
source for a particular MFC dll in one .h/.cpp pair? These questions
might appear very naive to you but if you could explain them, I would
be very grateful.

Thanks,
Ishan.

Joseph M. Newcomer

unread,
Feb 17, 2004, 12:15:25 PM2/17/04
to
The are several issues here. Some are easy, and some are made difficult by the incredibly
inadequate nature of C and C++.

The simplest answer is that your component has a single .h file which the user of that
component includes to use the component. This is different from how the component is
constructed, which could involve several interior classes and many implementation files.

One way to do this is to point the users at a \include subdirectory of your component, and
give them the name of the .h file to use. This .h file will include any files it needs.
The downside of this is that if any of the names used for your internal header files
conflict with names the client is already using, you will get serious malfunctions when
the wrong header file is read in the wrong context. So it probably won't compile.

The next solution to this is to give the client a single header file and a .lib file which
can be linked in. The internal structure that produced the .lib file is invisible to the
client, although it exists within the subproject that creates the .lib file. The problem
here is that if any function name, even those used internally, conflict with any names
used by the client software, you will get linker errors. Some of C++ simplifies this, and
you can additionally use the namespace directive to add another layer to try to make
conflicts unlikely.

You can replace the .lib file with a .lib file for a .dll, Now you only have to deal with
the conflicts of the exported symbols, but it introduces issues about things like
statically-linked MFC libraries vs. the shared MFC DLL library.

Finally, you can create an ActiveX control. There is a lot of gratuitous complexity in
this, but it is a standard infterface, and it does not have the namespace problems of the
other solutions.

Note however that all of these have one common theme: a single .h file as the interface
seen by the client, and this .h file has nothing in it that is not required by that
interface. There may be some .cpp file that also forms part of the exported interface, but
this is incidental (e.g., there is no client-visible .cpp file associated with a DLL).
Header files that are part of your implementation but are not needed for the user
interface should not be part of the interface specification.

C and C++ don't always make this easy. The primitive nature of the public/protected (or
public/private) model that C++ uses is pitiful (the correct solution is to provide for
multiple named views in interface files, an idea that dates back to the 1970s). The
unfortunate tendency to mix specification and implementation in header files is another
problem whose solution dates back decades but has been ignored. Consequently, we are left
with less-than-adequate tools to solve problems like this.

The only classes you would define in the header file are the classes the end user needs to
interface to the control. These classes may physically appear in a single header file, or
they may appear in header files which are included by the header file (always being aware
of the dangers of filename conflicts). Other classes would be treated as opaque and their
definitions would be invisible. Sometimes you can do this by simply declaring
class whatever;
in the header file and using pointers; in other cases you would do
typedef void * whatever;
and recast these pointers inside your implementation. There are other approaches, such as
using the generic HANDLE type to represent opaque pointers. Most of these are dictated by
the inadequacies of C and/or C++.

Put as little in the .h file and the class as you can reasonably manage. I've even seen
examples in C++ where the user exports a superclass to the client, and the implementation
is done as a subclass. This often requires virtual methods, which some people find to be
an issue of debate. The advantage is that none of the implementation methods appear in the
header file at all; essentially, there is never a "protected" part of the class. The
protected part appears only in the (invisible) subclass. This takes a lot more work to get
right.

In many cases, the decision to lump or to split is ultimately one of taste and
engineering. This is still an art form, not a mathematically-defined discipline. MFC lumps
classes for simplicity, so very few files need to be explicitly included. THe downside is
that you get massive header files that the compiler must parse and reparse every time they
are included (tens of thousands of lines). Hence, precompiled header technology to avoid
this cost of large header files.
joe

Joseph M. Newcomer [MVP]

Ishan De Silva

unread,
Feb 18, 2004, 1:03:56 AM2/18/04
to
Hi,

Thanks a lot for your time.
That was really informative.

Cheers,
Ishan.

Joseph M. Newcomer <newc...@flounder.com> wrote in message news:<11d330p11t6h4kcf8...@4ax.com>...

0 new messages