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

double inclusion of header files

2 views
Skip to first unread message

John Fisher

unread,
Mar 6, 2003, 12:50:28 AM3/6/03
to
My company uses code from a third party which is using the following
technique:

-----<outer.h>-------
#include <inner.h>
/* use some definitions from the first pass of inner.h */
#include <inner.h>
/* use some definitions from the second pass of inner.h */
-----------------------

-----<inner.h>-----
#ifndef SECOND_PASS
#define SECOND_PASS

/* first pass definitions */

#else

#ifndef INCLUDED_INNER_H
#define INCLUDED_INNER_H

/* second pass definitions */

#endif

#endif
----------------------

My compiler seems to only include inner.h once: the second pass definitions
are not available to later code. I have checked preprocessor output to
support this view. The compiler has no switches to alter this behaviour. An
earlier version of this compiler behaves differently: the second pass
definitions are available to code in outer.h after the second inclusion of
inner.h.

My third party code vendor appears to be using this technique because
inner.h contains various parameters that are more subject to change than
ordinary code and didn't want to create two such files.

My question is is there anything in the C90 standard to preclude the use of
this technique? In other words are compilers allowed to discard the second
inclusion of a header file? To put it yet another way, have I got an issue
with my compiler vendor or my third party code vendor?

My own interpretation of the C90 standard is that this technique should
actually work, although I don't think my third party code vendor's reasons
are sufficient to justify its use.
--
comp.lang.c.moderated - moderation address: cl...@plethora.net

Jack Klein

unread,
Mar 6, 2003, 1:55:36 PM3/6/03
to
On 06 Mar 2003 05:50:28 GMT, "John Fisher" <nos...@fakeisp.com> wrote
in comp.lang.c.moderated:

There is nothing in the C standard, C90 or C99, to prevent the
inclusion of a user-defined header more than once. Unless there is
some preprocessor macro that (typical "include guard") that prevents,
this is actually a serious conformance failure.

Some compilers have a pragma to allow the programmer, at his/her own
risk, to specify this behavior. If the compiler truly has no way to
turn this off you have a serious beef.

As for your third party code vendor, you have a beef with him anyway.
There might be impressionable youngsters reading this group so I won't
elaborate on my opinion of a vendor who uses this technique.

Really clever preprocessor tricks *very rarely* belong in production
quality code. I find it extremely hard to believe that any
professional coder could justify this as a vast improvement over
inner.h containing just these two lines:

#include "inner1.h"
#include "inner2.h"

Unless they're maintaining these files on punch cards, but I wouldn't
think they could justify that either.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://www.eskimo.com/~scs/C-faq/top.html
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++ ftp://snurse-l.org/pub/acllc-c++/faq

Douglas A. Gwyn

unread,
Mar 6, 2003, 1:55:38 PM3/6/03
to
John Fisher wrote:
> My compiler seems to only include inner.h once: ...

Sounds like it is getting the idempotency check wrong due to
seeing the #ifndef #define lock but not realizing that only
half the file is locked by that. Some compilers make this
check to speed things up, since they don't even bother to
open the header file if it has already been idempotent-
locked for the current translation unit.

> The compiler has no switches to alter this behaviour.

That's because it's a bug.

> My third party code vendor appears to be using this technique because
> inner.h contains various parameters that are more subject to change than
> ordinary code and didn't want to create two such files.

Unless you can get the compiler fixed quickly, your only
recourses (apart from changing compilers) are to preprocess
the translation units separately using a standard-conforming
standalone preprocessor, which is a lot of bother, or to
split that header into the two files that it really should
have been all along.

> ... are compilers allowed to discard the second


> inclusion of a header file?

Only if they have a *proof* from inspecting it the first
time that the second inclusion will have no effect.

those who know me have no need of my name

unread,
Mar 6, 2003, 1:56:02 PM3/6/03
to
in comp.lang.c.moderated i read:

>My company uses code from a third party which is using the following
>technique:
>
>-----<outer.h>-------
>#include <inner.h>
>/* use some definitions from the first pass of inner.h */
>#include <inner.h>
>/* use some definitions from the second pass of inner.h */
>-----------------------

[with macro magic to determine which portions of inner.h are included]

>My compiler seems to only include inner.h once:

this is an optimization of your compiler, one that might have a knob you
can adjust if you are lucky.

>My question is is there anything in the C90 standard to preclude the use of
>this technique?

nope. it's purely a function of your compiler vendor -- they are providing
you with a performance enhancement, one that has conditions where it isn't
appropriate of which the code vendor is tickling the major one.

>To put it yet another way, have I got an issue
>with my compiler vendor or my third party code vendor?

if your compiler is sufficiently popular and your code vendor is aware of
the problem but provides no workaround or app note on the issue then i'd
say that you have a code vendor issue. but in truth the code vendor is
doing nothing wrong so far as the standard is concerned, and so you also
have a problem with your compiler vendor if there's no knob to return it
to processing in a strictly conforming way.

--
bringing you boring signatures for 17 years

Hans-Bernhard Broeker

unread,
Mar 6, 2003, 1:56:07 PM3/6/03
to
John Fisher <nos...@fakeisp.com> wrote:

> -----<outer.h>-------
> #include <inner.h>
> /* use some definitions from the first pass of inner.h */
> #include <inner.h>
> /* use some definitions from the second pass of inner.h */
> -----------------------

Ouch. It's quite amazing what lengths people will go to to do silly
things with a simple tool, in the wrong ways.

> My compiler seems to only include inner.h once: the second pass definitions
> are not available to later code.

Some compilers optimize away multiple inclusion of the same header, if they
find the usual

#ifndef FOO
#define ...
#endif

stuff surrounding all non-comment contents of the file. They probably
wouldn't expect anybody to have added an #else line into the game, and
so they would fail in the way you describe it.

> I have checked preprocessor output to support this view.

Check it in more detail. There should be at least some indication
*why* it didn't expand the #else branch in the second run.

> My third party code vendor appears to be using this technique because
> inner.h contains various parameters that are more subject to change than
> ordinary code and didn't want to create two such files.

A silly plan, IMHO.

> My question is is there anything in the C90 standard to preclude the use of
> this technique?

For the standard header files themselves, it is explicitly forbidden.
No standard header's behaviour may depend on whether any other
standard header, including itself, has been #include'd before itself
or not, with the exception of <assert.h>

> In other words are compilers allowed to discard the second
> inclusion of a header file? To put it yet another way, have I got an issue
> with my compiler vendor or my third party code vendor?

With both, but for different reasons.

--
Hans-Bernhard Broeker (bro...@physik.rwth-aachen.de)
Even if all the snow were burnt, ashes would remain.

Dave Hansen

unread,
Mar 6, 2003, 1:56:12 PM3/6/03
to
On 06 Mar 2003 05:50:28 GMT, "John Fisher" <nos...@fakeisp.com> wrote:

>My company uses code from a third party which is using the following
>technique:

First, this "technique" is ugly and hideous.

>
>-----<outer.h>-------
>#include <inner.h>
>/* use some definitions from the first pass of inner.h */
>#include <inner.h>
>/* use some definitions from the second pass of inner.h */
>-----------------------
>
>-----<inner.h>-----
>#ifndef SECOND_PASS
>#define SECOND_PASS

[...]
>#else
[...]


>#endif
>----------------------
>
>My compiler seems to only include inner.h once: the second pass definitions
>are not available to later code. I have checked preprocessor output to
>support this view. The compiler has no switches to alter this behaviour. An
>earlier version of this compiler behaves differently: the second pass
>definitions are available to code in outer.h after the second inclusion of
>inner.h.

It appears your compiler vendor has implemented an optimization
incorrectly.

It is a very common practice to structure header files thus:

#ifndef symbol
#define symbol
... header file definitions ...
#endif

They are designed this way to prevent multiple inclusions. It is very
common for compilers to note this structure the first time they
#include a file, and then not bother to #include (and lex and parse)
the file if it is ever #included again. This saves a significant
amount of time.

It appears your vendor has implemented the optimization too simply.
They saw the #ifdef, the #define, and the final #endif, but failed to
notice the #else.

[...]

>My own interpretation of the C90 standard is that this technique should
>actually work, although I don't think my third party code vendor's reasons
>are sufficient to justify its use.

I concur. Indeed, I believe the standard says it *must* work.

Regards,

-=Dave
--
Change is inevitable, progress is not.

Thad Smith

unread,
Mar 6, 2003, 1:56:18 PM3/6/03
to
John Fisher wrote:
>
> My company uses code from a third party which is using the following
> technique:
>
> -----<outer.h>-------
> #include <inner.h>
> /* use some definitions from the first pass of inner.h */
> #include <inner.h>
> /* use some definitions from the second pass of inner.h */
> -----------------------
....

> My compiler seems to only include inner.h once: the second pass definitions
> are not available to later code. I have checked preprocessor output to
> support this view. The compiler has no switches to alter this behaviour. An
> earlier version of this compiler behaves differently: the second pass
> definitions are available to code in outer.h after the second inclusion of
> inner.h.

The usage is conforming. The compiler isn't. Contact your compiler
vendor.

I have used this technique, but for a different purpose: to aggregate
definitions in a header file used to generate multiple const lookup
tables and associate error names with associated message strings.

> My question is is there anything in the C90 standard to preclude the use of
> this technique?

No.

Thad

Rob

unread,
Mar 11, 2003, 11:10:03 AM3/11/03
to
"John Fisher" <nos...@fakeisp.com> wrote in message
news:clcm-2003...@plethora.net...


OK; there are two issues here.

Firstly, the macro trickery you describe above is legal, but it is also very
bad style. At the least, I'd suggest a better approach by the software
vendor would be to simply use two separate header files. From what you
describe, the reason they want to use one header file is spurious, and
probably based on someone's laziness.

As is, those headers would be bad dream for a maintainer. The fact that
you have to use macro trickery in the file that includes this header means
it
is *your* maintenance bad dream. I would suggest a pointed message to
your third party supplier about "spaghetti code that unnecessarily relies on
preprocessor hackery is not a way to encourage customers, who care
about quality of *their* products, to use your product. The fact that the
code does NOT WORK with compiler X is also not a selling point".

Secondly, even though the code is a maintainers bad dream, the compiler
(or preprocessor) should still do the right thing with it. You have a
compiler bug to report to that vendor.

Allan W

unread,
Apr 2, 2003, 8:50:01 PM4/2/03
to
> John Fisher wrote:
> > -----<inner.h>-----
> > #ifndef SECOND_PASS
> > #define SECOND_PASS
> > /* first pass definitions */
> >
> > #else
> > #ifndef INCLUDED_INNER_H
> > #define INCLUDED_INNER_H
> > /* second pass definitions */
> > #endif
> > #endif
> > ----------------------
> > My compiler seems to only include inner.h once: ...

"Douglas A. Gwyn" <DAG...@null.net> wrote


> That's because it's a bug.

> Unless you can get the compiler fixed quickly, your only
> recourses (apart from changing compilers) are to preprocess
> the translation units separately using a standard-conforming
> standalone preprocessor, which is a lot of bother, or to
> split that header into the two files that it really should
> have been all along.

I don't have that particular compiler, so this is only a guess
and I can't even check if it works or not.

Until you get a corrected compiler, you might be able to work
around the bug by creating surrounding #if's that do not match
the "idempotent" (include-guard) pattern.

-----<inner.h>-----
/* -> */ #ifndef DO_NOT_DEFINE_THIS_SYMBOL


#ifndef SECOND_PASS
#define SECOND_PASS
/* first pass definitions */

#else
#ifndef INCLUDED_INNER_H
#define INCLUDED_INNER_H
/* second pass definitions */

#endif
#endif
/* -> */ #endif
----------------------

The two lines marked with /* -> */ are new. These lines look
similar to the standard include guard, but since the symbol
DO_NOT_DEFINE_THIS_SYMBOL is never defined, the compiler
ought to realize that it isn't really an include guard after
all. The other #if's nested inside probably won't be
considered at all. The result is that the whole file is
loaded every time it's #included, and the appropriate sections
are actually processed.

Hope it works! Good luck.

0 new messages