[gnu.gcc] Is "#pragma once" necessary?

dld@F.GP.CS.CMU.EDU (David Detlefs) (07/26/89)

I would like to make an argument that #pragma once is unnecessary, and
perhaps even dangerous.  First, calling "#pragma once" a "pragma"
conveys the impression that it affects only efficiency, never
correctness.  This is not true; if foo.h contains ifdefs that depend
on the state of preprocessor variables, separate includes might
produce different results.  A user could put a #pragma once in such a
foo.h, not realizing that it would affect the semantics of his
program.  (Note that when I say "conveys the impression" I do not mean
to say that it is written in the Ansi C standard that pragmas shall
never affect the meaning of an Ansi C program, rather that the term
"pragma" has been used often in other contexts with that meaning, so
that programmers might expect that to be true.)

An alternative, gleaned from discussion on various netnews groups:

why is "#pragma once" preferable to the #ifndef convention?  The
standard argument is that if we use #ifndef's, we still have to open
and process a file on inclusions after the first, to find out that
it's body is #ifndef'd.

Instead, we could modify cpp to keep a table of the files it has
processed.  When a file is read for the first time, cpp checks to see
if it is of the form

foo.h:
--------------------------------------------------
<comments and whitespace>*
#ifndef FOO_H
<anything>*
#endif
<comments and whitespace>*
--------------------------------------------------

If so, then it is associated in the table with the variable FOO_H.
Each included file name is looked up in this table before the file is
opened.  If found, we check the the current value of the associated
variable; if it is defined, we skip the include without opening and
processing the file.

This seems like it should be just as efficient as "#pragma once,"
without changing any semantics, and preserving compatibility with
existing code.  I stand of course eager to hear reasons I have
overlooked that make "#pragma once" preferable.

NOTE: this is not my idea; I just don't know who to credit it to.  If
the person who made the post that I read it in (perhaps in this
newsgroup?) will send me mail, I will credit him/her in any subsequent
discussion, because I think this is a great idea.

--
Dave Detlefs			Any correlation between my employer's opinion
Carnegie-Mellon CS		and my own is statistical rather than causal,
dld@cs.cmu.edu			except in those cases where I have helped to
				form my employer's opinion.  (Null disclaimer.)

grunwald@flute.cs.uiuc.edu (Dirk Grunwald) (07/26/89)

hacking cpp to recognize #ifndef sounds like even more of a problem;
people have different conventions on where they place the #ifndef,
the #define and so on.

Rather than play tricks like this, I think that having #requires and
#provides would be a lot cleaner, albeit less portable. One could
have syntax akin to include, e.g:

bar.c: #require <foo.h>
...
foo.h: #provide <foo.h>

where the #provides is really redundant. You could also play more
elaborate games with such a mechanism, e.g., allow arbitrary
strings to be required and provided:

bar.c: #require "valid machine description"

which would be a cleaner way of barfing on bad configurations than;

#ifndef VALID_MACHINE_DESCRIPTION
  You don't have a valid description!
#endif

Since gnu CPP can be easily distributed, portability isn't much of an
issue -- if someone uses your library, tell them to also use gnu CPP.
--
Dirk Grunwald -- Univ. of Illinois 		  (grunwald@flute.cs.uiuc.edu)

dld@F.GP.CS.CMU.EDU (David Detlefs) (07/26/89)

In response to my suggestion about modify cpp to get the efficiency of
"#pragma once" without the change in semantics, Dirk Grunwald writes:

>hacking cpp to recognize #ifndef sounds like even more of a problem;
>people have different conventions on where they place the #ifndef,
>the #define and so on.

I don't see the problem.  cpp can certainly distinguish between
comments and actual program text.  If you write a foo.h file of the form

--------------------------------------------------
<comments and whitespace>*
#ifndef FOO_H
<anything at all -- note that it doesn't matter where, or even *if*,
the #define FOO_H occurs>
#endif
<comments and whitespace>*
--------------------------------------------------
then cpp is certainly safe in disregarding subsequent includes of
foo.h that occur while FOO_H is defined.  If you have anything other
than a comment or whitespace before the #ifndef, or after the
corresponding #endif, then cpp might not be safe in ignoring
subsequent includes.  There is certainly no difficulty in recognizing
file of this form, and I believe that pretty much all files using the
#ifndef convention fit it.

As for #requires and #provides, I think we have to recognize that
we're going to exist for a long time in a sea of previously existing
code, and we shouldn't change a perfectly good mechanism without good
reason.  I fail to see adequate advantages with #requires and
#provides (though this does not of course imply thay such advantages
do not exist.)




--
Dave Detlefs			Any correlation between my employer's opinion
Carnegie-Mellon CS		and my own is statistical rather than causal,
dld@cs.cmu.edu			except in those cases where I have helped to
				form my employer's opinion.  (Null disclaimer.)

dld@F.GP.CS.CMU.EDU (David Detlefs) (07/28/89)

Because I really admire whoever thought of this idea, I'll continue to
try to explain it better:

John Plocher (plocher@sun.Eng.Sun.Com?) writes
>But what if I don't write it in the magic form you describe?  Then your
>cute trick breaks.
>
>You seem to use FOO_H as the token used for the #ifndef - I use _FOO_H_.
>curses.h may well use #ifndef mvwputc, and serialio.h may use #ifndef IOGETBAUDRATE

I'm sorry: in my example, I intended FOO_H to stand for any
preprocessor variable name at all, not literally "FOO_H."  I thought
this would be clear.  The first time through, you associate the
variable name with the file name; on subsequent inclusios, you see if
the variable is defined before opening the file.

>then how can you distinguish between this and something that happens to have a #ifndef
>at the beginning which is NOT intended to be used as a Only One Inclusion flag?

The point is that the intention doesn't matter; if a file is of this
form, then it only needs to be included once (if the #ifndef variable
is defined at the time of later inclusions): subsequent inclusions
will result in no non-comment text.  Which is exactly why #ifndef is
used this way.

You are right that "#pragma once" says the same thing more succintly;
my objections are (again) 1) that a "pragma" is usually taken to mean
something that affects performance and not semantics, while pragma
once may definitely affect semantics, and 2) that in the absence of
really good reasons such as performance, which my proposal solves, we
shouldn't arbitrarily abandon existing mechanisms.
--
Dave Detlefs			Any correlation between my employer's opinion
Carnegie-Mellon CS		and my own is statistical rather than causal,
dld@cs.cmu.edu			except in those cases where I have helped to
				form my employer's opinion.  (Null disclaimer.)