[comp.lang.c] A solution to the multiple inclusion problem

nagle@well.UUCP (John Nagle) (10/23/89)

      The problem is well-known.  It would be desirable if all include files
themselves included everything they needed, so that order of inclusion 
was taken care of automatically.  But, of course, this results in multiple
inclusion of the same files, which causes problems.  A common work-around
for this problem is to use a construct like the following in each include file:

	#ifndef XXX
	#define XXX
	...content...
	#endif

This works, but on the second inclusion, the file still has to be read and
parsed, at least by the level of processing that reads "#" statements.
(Many newer compilers do "#" processing in the same pass as main compilation,
so referring to a "preprocessor" in this context is not necessarily correct.)
With widespread use of this technique within library files, some files may
be read a large number of times, mostly to be ignored.  This slows compilation.
The problem is especially severe in large C++ programs, where large numbers of
header files are necessary, and nested header files are not at all uncommon.

      It's been proposed that the semantics of "#include" be changed to 
avoid all multiple inclusion.  But this is controversial, and would require
ANSI approval.

      I propose a solution via compiler optimization.
The compiler should behave as follows:

	1.  If, when reading an "included" file, there are
	    no non-comment statements before the first "#ifndef"
	    (if any), and no non-comment statements after the "#endif"
	    matching said "#ifndef", the compiler shall associate
	    the tag found on the "#ifndef" line with the name of the
	    "#include" file.

	2.  When processing an "#include" statement, if the file has
	    an associated tag as defined in 1) above, and the tag is
	    defined (in the sense of "#define") the file shall not be
	    included.

This is a completely compatible solution to the problem.  Old compilers
will compile include files written with the "ifndef" convention correctly,
but slowly, and new compilers will do it faster.  No standardization
action is required.  Any implementor can install this now, and speed up
their product.

      One could argue for a more elegant but less compatible solution, but
the political hassles aren't worth it.

					John Nagle

tada@athena.mit.edu (Michael J Zehr) (10/24/89)

In article <14240@well.UUCP> nagle@well.UUCP (John Nagle) writes:
>[problem of multiple exclusion, generally solved by:]
>	#ifndef XXX
>	#define XXX
>	...content...
>	#endif
>This works, but on the second inclusion, the file still has to be read and
>parsed, at least by the level of processing that reads "#" statements.
>[slowing compilation, particularly in C++ programs]

>[proposed compiler optimization binding the 'XXX' defined above to the
>included file and not including it a second time.]

there's another solution you can manage without having to get your
compiler vendor to make such an extension:

<foo.h:>
#ifndef FOO
#define FOO
#include "foo_real.h"
#endif

admittedly there's an extra file open for the first time reading
through, and you still have to open files when #including the second
time, but it's not as bad as having to read the entire file in
(particularly if you have some really *large* files).  library files
could be changed to this with relatively little effort and no compiler
changes, and users' header files for large systems could be changed to
work faster *today* without having to wait for the vendors to decide to
do this.

(unless i'm missing something obvious and being really stupid this
works....)

-michael j zehr

rsalz@bbn.com (Rich Salz) (10/24/89)

Gee, if
	#ifndef _HAVE_FOO_H_
	... contents of foo.h ...
	#define _HAVE_FOO_H_
	#endif	/* _HAVE_FOO_H_ */

is too slow, then do this:
	#ifndef _HAVE_FOO_H_
	#include <hidden_real_name_of_foo.h>
	#define _HAVE_FOO_H_
	#endif	/* _HAVE_FOO_H_ */

John's rules about the first #ifndef after the first comment sound much
too complicated for me -- I have enough problem with election day...
	/r$
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.
Use a domain-based address or give alternate paths, or you may lose out.

crowl@cs.rochester.edu (Lawrence Crowl) (10/24/89)

In article <14240@well.UUCP> nagle@well.UUCP (John Nagle) notes three
solutions to the multiple file inclusion problem:

(1) An include file protects itself via #ifndef on a symbol that it defines.
    This causes the file to be read multiple times.

(2) Modifying the semantics of #include to only include a file once.  This is
    incompatible with the current definition of #include.

(3) His proposal for modifying the implementation of #include to recognize the
    idiom in (1).  Implementations need not read a file twice.

However, a solution to the multiple inclusion problem already exists.  It is
not necessary (nor desireable in my opinion) to modify #include semantics or
implementations.  The solution is as follows.

Each include file defines a symbol (preferably related to its name).  For
example, in foo.h:

        #define foo_h
        ...

Each file that includes foo.h, protects the inclusion with a #ifndef:

        ...
        #ifndef foo_h
        #include "foo.h"
        #endif foo_h
        ...

Programmers of include files can further protect against multiple inclusion
by using the standard mechanism:

        #ifndef foo_h
        #define foo_h 
        ...
        #endif foo_h

This solution has the following properties:
- The compiler reads include files exactly once.
- No modifications to current systems are required.
- Includes are three lines long instead of one.
- A small (really) amount of additional programmer effort is required.

I have used this solution as a matter of course since shortly after I learned
to program in C.  It is an obvious solution, and leads me to wonder why it is
not common practice.  Any explainations?
-- 
  Lawrence Crowl		716-275-9499	University of Rochester
		      crowl@cs.rochester.edu	Computer Science Department
...!{allegra,decvax,rutgers}!rochester!crowl	Rochester, New York,  14627

hascall@atanasoff.cs.iastate.edu (John Hascall) (10/24/89)

In some article with a silly huge id#, Lawrence Crowl writes:
}In article <14240@well.UUCP> nagle@well.UUCP (John Nagle) notes three
}solutions to the multiple file inclusion problem:
 
}(2) Modifying the semantics of #include to only include a file once.  This is
}    incompatible with the current definition of #include.
 
}  [yet another scheme...]

    Since the impending ANSI standard requires that including a file more
    than once have exactly the same effect as including it once...why can't
    a compiler just ignore #includes for files it has already #included???
    (at least for the "standard" includes)
    
    Any comments from the ANSI mavens?


    I know some people use stuff like:

 main.c:                              subr.c:
    #define MAINDEF 1                    #define MAINDEF 0
    #include "vars.h"                    #include "vars.h"
    main()                               subrtn()
    { ...                                { ...

    but, does anyone do this thing in the *same* file??

John Hascall

tneff@bfmny0.UU.NET (Tom Neff) (10/24/89)

In article <1659@atanasoff.cs.iastate.edu> hascall@atanasoff.UUCP (John Hascall) writes:
>    Since the impending ANSI standard requires that including a file more
>    than once have exactly the same effect as including it once...why can't
>    a compiler just ignore #includes for files it has already #included???
>    (at least for the "standard" includes)

Including standard HEADER files should be idempotent.  Back here in the
real world there are plenty of uses for including a file multiple times
with a desired substantial effect on each inclusion.  Examples include
program generated data tables, copyright strings, and machine dependent
code sequences.  Any compiler that unconditionally ignored an include file
on the second mention would be horribly broken.
-- 
"My God, Thiokol, when do you      \\    Tom Neff
want me to launch?  Next April?"   \\    tneff@bfmny0.UU.NET

ken@cs.rochester.edu (Ken Yap) (10/24/89)

|(1) An include file protects itself via #ifndef on a symbol that it defines.
|    This causes the file to be read multiple times.

But is this really as inefficient as people think? I tried the
following on a Sun-4/60

% wc grammar0.cc
     932    2944   19700 grammar0.cc
% g++ -I../h -E grammar0.cc | wc
    3728    8219   63497
% time g++ -I../h -E grammar0.cc > /tmp/foo.cc
0.4u 0.3s 0:01 44% 0+208k 0+9io 0pf+0w

Looks pretty insignificant compared to parsing and CG time.

Just to prove that multiple inclusions were attempted

% grep '#' /tmp/foo.cc | sort +2 -3 | uniq -2 -c
   2 # 1 "../h/pg_types.h" 1
   1 # 42 "../h/pg_types.h"
   1 # 1 "/usr/su/lib/g++-include/BitSet.h" 1
   2 # 28 "/usr/su/lib/g++-include/BitSet.h" 2
   1 # 1 "/usr/su/lib/g++-include/File.h" 1
   3 # 27 "/usr/su/lib/g++-include/File.h" 2
   1 # 1 "/usr/su/lib/g++-include/builtin.h" 1
   3 # 48 "/usr/su/lib/g++-include/builtin.h" 2
   1 # 1 "/usr/su/lib/g++-include/math.h" 1
   1 # 126 "/usr/su/lib/g++-include/math.h" 2
   2 # 1 "/usr/su/lib/g++-include/std.h" 1
   1 # 225 "/usr/su/lib/g++-include/std.h"
   2 # 1 "/usr/su/lib/g++-include/stddef.h" 1
   1 # 59 "/usr/su/lib/g++-include/stddef.h"
   1 # 1 "/usr/su/lib/g++-include/stdio.h" 1
   2 # 1 "/usr/su/lib/g++-include/stream.h" 1
   1 # 160 "/usr/su/lib/g++-include/stream.h"
   1 # 27 "/usr/su/lib/g++-include/stream.h" 2
   2 # 1 "/usr/su/lib/g++-include/values.h" 1
   2 # 92 "/usr/su/lib/g++-include/values.h"
   1 # 1 "error.h" 1
   2 # 1 "grammar.h" 1
   2 # 11 "grammar.h" 2
   1 # 127 "grammar.h"
   4 # 13 "grammar.h" 2
   1 # 1 "grammar0.cc"
  10 # 1 "grammar0.cc" 2
   2 # 1 "item.h" 1
   1 # 98 "item.h"
   2 # 1 "option.h" 1
   1 # 40 "option.h"
   1 # 1 "pg.h" 1
   2 # 10 "pg.h" 2
   1 # 1 "pggram.h" 1
   1 # 1 "predict.h" 1
   1 # 9 "predict.h" 2
   1 # 1 "production.h" 1
   3 # 10 "production.h" 2
   5 # 1 "symbol.h" 1
   1 # 10 "symbol.h" 2
   4 # 140 "symbol.h"
   1 # 9 "symbol.h" 2
   4 # 1 "symset.h" 1
   2 # 10 "symset.h" 2
   3 # 84 "symset.h"
   1 # 9 "symset.h" 2
   1 # 1 "symtab.h" 1
   1 # 9 "symtab.h" 2
   3 # 1 "termset.h" 1
   2 # 50 "termset.h"
   1 # 9 "termset.h" 2

Some of the .h files are pretty hefty, as you can see from the size of
the expanded source.

I don't think I will lose any sleep over what cpp is doing.

matthew@sunpix.UUCP ( Sun Visualization Products) (10/24/89)

In article <15316@bloom-beacon.MIT.EDU> tada@athena.mit.edu (Michael J Zehr) writes:
|In article <14240@well.UUCP> nagle@well.UUCP (John Nagle) writes:
|>[problem of multiple exclusion, generally solved by:]
|>	#ifndef XXX
|>	#define XXX
|>	...content...
|>	#endif
|>This works, but on the second inclusion, the file still has to be read and
|>parsed, at least by the level of processing that reads "#" statements.
|>[slowing compilation, particularly in C++ programs]
|
|>[proposed compiler optimization binding the 'XXX' defined above to the
|>included file and not including it a second time.]
|
|there's another solution you can manage without having to get your
|compiler vendor to make such an extension:
|
|<foo.h:>
|#ifndef FOO
|#define FOO
|#include "foo_real.h"
|#endif
|
|admittedly there's an extra file open for the first time reading
|through, and you still have to open files when #including the second
|time, but it's not as bad as having to read the entire file in
|(particularly if you have some really *large* files).  library files
|could be changed to this with relatively little effort and no compiler
|changes, and users' header files for large systems could be changed to
|work faster *today* without having to wait for the vendors to decide to
|do this.
|
|(unless i'm missing something obvious and being really stupid this
|works....)
|
|-michael j zehr

or if your sure all your header files use the:

#ifndef XXX
#define XXX
...content...
#endif

constructs, you could just alter you source code a bit, and do an '#ifndef', '#endif' around
each '#include'.  This would require adding two lines for each included file, and no extra files
open.


/*
 * Start of source code
 */

#ifndef STDIO.H
#include <stdio.h>
#endif
#ifndef CTYPE.H
#include <ctype.h>
#endif


     Now, if only library writers used the '#ifndef', '#endif' construct consistantly.



-- 
Matthew Lee Stier                            |
Sun Microsystems ---  RTP, NC  27709-3447    |     "Wisconsin   Escapee"
uucp:  sun!mstier or mcnc!rti!sunpix!matthew |
phone: (919) 469-8300 fax: (919) 460-8355    |

meissner@dg-rtp.dg.com (Michael Meissner) (10/24/89)

In article <1659@atanasoff.cs.iastate.edu>
hascall@atanasoff.cs.iastate.edu (John Hascall) writes:

>      Since the impending ANSI standard requires that including a file more
>      than once have exactly the same effect as including it once...why can't
>      a compiler just ignore #includes for files it has already #included???
>      (at least for the "standard" includes)
>      
>      Any comments from the ANSI mavens?

Only the include files specified by standard (stdio.h, string.h, etc.)
are required to work when included multiple times (ie, there must be
some sort of guard around parts that can not be redeclared, like
typedefs and structures).  No such requirement is mandated for any
other include file.
--

Michael Meissner, Data General.				If compiles where much
Uucp:		...!mcnc!rti!xyzzy!meissner		faster, when would we
Internet:	meissner@dg-rtp.DG.COM			have time for netnews?

mgordon@lotus.com (PCSD Mac) (10/24/89)

How about a new preprocessor directive that means "include this file only 
if it hasn't already been included", say "#require"?  The "preprocessor"
would simply keep a table of files that have already been included and use 
it to avoid including the same #required file more than once.  There will 
always be files you really do want to #include multiple times, so you can't
change the meaning of #include.

I believe that would do the trick, and it wouldn't break any existing 
programs.  If you use this construct though, you can't go back to compilers 
that don't support it.

davidm@uunet.UU.NET (David S. Masterson) (10/24/89)

In article <14240@well.UUCP> nagle@well.UUCP (John Nagle) writes:

   (Many newer compilers do "#" processing in the same pass as main
   compilation, so referring to a "preprocessor" in this context is not
   necessarily correct.)

I suppose that this is a performance enhancement and that now "#" processing
is being considered so much a part of the language that compiler writers feel
justified in doing this.  However, doesn't this limit flexibility?  Because of
this, the problem being discussed results in having to change the compiler as
opposed to changing the (probably more simple) preprocessor.

--
===================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mt. View, CA  94043
===================================================================
		"Nobody here but us chickens..."

davidm@uunet.UU.NET (David S. Masterson) (10/25/89)

In article <1989Oct23.191634.6345@cs.rochester.edu> crowl@cs.rochester.edu (Lawrence Crowl) writes:

   [discussion of standard include mechanism]

   - A small (really) amount of additional programmer effort is required.

Yeh, but programmers are notoriously lazy.  Computers do stuff like this far
better than humans do, once they've been taught how.  Perhaps its time to
teach the computers.

--
===================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mt. View, CA  94043
===================================================================
		"Nobody here but us chickens..."

davidm@uunet.UU.NET (David S. Masterson) (10/25/89)

In article <1659@atanasoff.cs.iastate.edu> hascall@atanasoff.cs.iastate.edu (John Hascall) writes:

       Since the impending ANSI standard requires that including a file more
       than once have exactly the same effect as including it once...why can't
       a compiler just ignore #includes for files it has already #included???
       (at least for the "standard" includes)

       Any comments from the ANSI mavens?

What????  (I hope this isn't true)

Doesn't the "#if" mechanism suggest that it is very possible to include a file
more than once and have different things happen?  For instance (not a really
good example, though):

setup.h:				main.c:
#ifdef GENERIC				#define GENERIC
#define INTSIZE 16			#include "setup.h"
#endif
#ifdef VAX				processor.c
#define INTSIZE 32			#define VAX
#endif					#include "setup.h"

--
===================================================================
David Masterson					Consilium, Inc.
uunet!cimshop!davidm				Mt. View, CA  94043
===================================================================
		"Nobody here but us chickens..."

jacob@gore.com (Jacob Gore) (10/25/89)

/ comp.lang.c / mgordon@lotus.com (PCSD Mac) / Oct 24, 1989 /
How about a new preprocessor directive that means "include this file only 
if it hasn't already been included", say "#require"?  The "preprocessor"
would simply keep a table of files that have already been included and use 
it to avoid including the same #required file more than once.  There will 
always be files you really do want to #include multiple times, so you can't
change the meaning of #include.
----------

Objective-C uses "#import" for just this.  It's very convenient.  (And
portable to all Objective-C implementations:-)

Jacob
--
Jacob Gore		Jacob@Gore.Com			boulder!gore!jacob

sartin@hplabsz.HPL.HP.COM (Rob Sartin) (10/25/89)

In article <1659@atanasoff.cs.iastate.edu> hascall@atanasoff.UUCP (John Hascall) writes:
>    Since the impending ANSI standard requires that including a file more
>    than once have exactly the same effect as including it once...why can't
>    a compiler just ignore #includes for files it has already #included???
>    (at least for the "standard" includes)

That introduces some potential oddities for <assert.h> which doesn't try
to prevent from being included twice.  My C++ 2.0 assert.h includes a
comment that says:

/* This header file intentionally has no wrapper, since the user
*  may want to re-include it to turn off/on assertions for only
*  a portion of the source file.
*/

>    but, does anyone do this thing in the *same* file??

=begin foo.c
#define NDEBUG
#include <assert.h>
int tested_function()
{
...
}

#undef NDEBUG
#include <assert.h>
int untested_function()
{
}
=end foo.c

Rob Sartin			internet: sartin@hplabs.hp.com
Software Technology Lab 	uucp    : hplabs!sartin
Hewlett-Packard			voice	: (415) 857-7592

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/25/89)

In article <14240@well.UUCP> nagle@well.UUCP (John Nagle) writes:
>      It's been proposed that the semantics of "#include" be changed to 
>avoid all multiple inclusion.  But this is controversial, and would require
>ANSI approval.

It's not especially controversial, because as you imply it would be a
change to a well-defined characteristic of the C language.  Thus when
it was proposed to X3J11, we had little difficulty in determining that
the proposal must be rejected.  Several people attested to the fact
that they have existing code that requires the existing semantics.

>      I propose a solution via compiler optimization.

Your solution does not at all seem to me to preserve existing semantics.

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/25/89)

In article <1659@atanasoff.cs.iastate.edu> hascall@atanasoff.UUCP (John Hascall) writes:
-    Since the impending ANSI standard requires that including a file more
-    than once have exactly the same effect as including it once...why can't
-    a compiler just ignore #includes for files it has already #included???
-    (at least for the "standard" includes)

It can, but ONLY for the standard headers.

mustard@sdrc.UUCP (Sandy Mustard) (10/25/89)

A solution to multiple include file inclusion that I propose would be:

#exit

thus you could write:

#ifdef this_h_file
#exit
#define this_h_file
...
#endif

The preprocessor/compiler on seeing the #exit directive could ignore everything
following it in the file (and could simply close the file.)

Sandy Mustard
mustard@sdrc.UU.NET

shap@delrey.sgi.com (Jonathan Shapiro) (10/25/89)

Why does everybody feel compelled to reinvent this wheel?

The current most widley accepted solution for single inclusion is to
insert a pragma into the header file:

   #pragma once

Jonathan Shapiro
Synergistic Computing Associates

shap@delrey.sgi.com (Jonathan Shapiro) (10/25/89)

Okay, here's another cute trick.

Have an include file called include-files.h, which contains things
like

   #define FRED_H "fred.h"
   #define WILMA_H "wilma.h"

include them by doing the following in all source files:

   #include "include-files.h"  // done once in all source files
  
   #include FRED_H
   #include ...

inside FRED_H do the following:

   #ifdef FRED_H
   #undef FRED_H
   #define FRED_H /dev/null
   #endif

This works, doesn't require much overhead, and can be automatically
done applied to existing code by a fairly simple shell script.

Jonathan S. Shapiro
Synergistic Computing Associates

mds@wang.UUCP (Marc San Soucie) (10/25/89)

Lawrence Crowl writes:

> Each include file defines a symbol (preferably related to its name).  For
> example, in foo.h:
>         #define foo_h
>         ...
> Each file that includes foo.h, protects the inclusion with a #ifndef:
>         ...
>         #ifndef foo_h
>         #include "foo.h"
>         #endif foo_h
>         ...
> Programmers of include files can further protect against multiple inclusion
> by using the standard mechanism:
>         #ifndef foo_h
>         #define foo_h 
>         ...
>         #endif foo_h


Then when programmers of programs can count on header files which follow such
conventions, top-level programs can use constructs like the second one to
insure that they do not also include extra copies of include files.

This is not an insignificant issue when file opens are slow, such as across
some networked file systems. Admittedly compiling across a network is kind of
goofy, but mature systems shouldn't impose such restrictions on their users.

    Marc San Soucie
    Massachusetts
    mds@wang.wang.com

djones@megatest.UUCP (Dave Jones) (10/25/89)

From article <946@friar-taac.UUCP), by matthew@sunpix.UUCP ( Sun Visualization Products):
) In article <15316@bloom-beacon.MIT.EDU) tada@athena.mit.edu (Michael J Zehr) writes:
))In article <14240@well.UUCP) nagle@well.UUCP (John Nagle) writes:
)))[problem of multiple exclusion, generally solved by:]
)))	#ifndef XXX
)))	#define XXX
)))	...content...
)))	#endif
)))This works, but on the second inclusion, the file still has to be read and
)))parsed ... 

I think you'll live a happier, more productive life, if you do it
this way...


	#ifndef XXX
	...content...
	#define XXX
	#endif


It makes no difference where you put the #define, unless you have
files which define recursive data-structures and thus potentially
refer to each other. (You use "struct foo*" before "struct foo" is
actually defined.) If it gets screwed up, and each file includes the
other, and if the the #define is at the top, trying to figure out what went
wrong can make your life a living hell.

bph@buengc.BU.EDU (Blair P. Houghton) (10/25/89)

How about ( in <foo.h>):

	#pragma Never_Again

Which tells the BlairTech/ANSI1.0 compiler that when it hits
a #include to read this file again, it should just ignore it.

				--Blair
				  "Which is what the original
				   poster was saying, only in an
				   'I want it standardized, maybe'
				   manner..."

gsf@ulysses.homer.nj.att.com (Glenn Fowler[drew]) (10/25/89)

In article <470005@gore.com>, jacob@gore.com (Jacob Gore) writes:
>> / comp.lang.c / mgordon@lotus.com (PCSD Mac) / Oct 24, 1989 /
>> How about a new preprocessor directive that means "include this file only 
>> if it hasn't already been included", say "#require"?  The "preprocessor"
>> would simply keep a table of files that have already been included and use 
>> it to avoid including the same #required file more than once.  There will 
> 
> Objective-C uses "#import" for just this.  It's very convenient.  (And
> portable to all Objective-C implementations:-)

the file equivalence relation may be hard to define, e.g., for unix, given

	ln x.h y.h

will #require "y.h" include y.h following #include "x.h"?
are "y.h" and "./y.h" the same file?
are "y.h" and "/home/gsf/news/y.h" the same file?
will #require "stdio.h" include stdio.h after #include <stdio.h>?

the various #include "..." implementations further cloud the issue
-- 
Glenn Fowler    (201)-582-2195    AT&T Bell Laboratories, Murray Hill, NJ
uucp: {att,decvax,ucbvax}!ulysses!gsf       internet: gsf@ulysses.att.com

jacob@gore.com (Jacob Gore) (10/25/89)

/ comp.lang.c / gsf@ulysses.homer.nj.att.com (Glenn Fowler[drew]) / Oct 24 /
>>> / comp.lang.c / mgordon@lotus.com (PCSD Mac) / Oct 24, 1989 /
>>> How about a new preprocessor directive that means "include this file only 
>>> if it hasn't already been included", say "#require"?  The "preprocessor"
>>> would simply keep a table of files that have already been included and use 
>>> it to avoid including the same #required file more than once.
>>[...]
>the file equivalence relation may be hard to define, e.g., for unix, given
>
>	ln x.h y.h

You are including files, not filenames.  So instead of keeping track of
files by their names, you do it by <device, inode> pair, or whatever it is
on your system that uniquely identifies a file.

> will #require "y.h" include y.h following #include "x.h"?

No.  (I tried it on NeXT's Objective-C compiler, and it didn't.)

> are "y.h" and "./y.h" the same file?

Yes.

> are "y.h" and "/home/gsf/news/y.h" the same file?

That I don't know :-)

> will #require "stdio.h" include stdio.h after #include <stdio.h>?

If `#require "stdio.h"' is looking to include /usr/include/stdio.h (or
whatever file `<stdio.h>' refers to).

Jacob
--
Jacob Gore		Jacob@Gore.Com			boulder!gore!jacob

utoddl@uncecs.edu (Todd M. Lewis) (10/25/89)

In article <1989Oct24.153611.12168@lotus.com>, mgordon@lotus.com (PCSD Mac) writes:
> 
> How about a new preprocessor directive that means "include this file only 
> if it hasn't already been included", say "#require"?  The "preprocessor"
> would simply keep a table of files that have already been included and use 
> it to avoid including the same #required file more than once.  There will 
> always be files you really do want to #include multiple times, so you can't
> change the meaning of #include.

Could this not be done with a compiler switch?  You could switch it
off if you really did have to include a file multiple times, but
this doesn't happen too often in Real Life (not mine anyway).

Actually, the compiler I use lets me dump its symbol table to a 
file.  Later, I compile with a switch which says "use this here file
to preload your symbol table", and I don't have to process all
those include files every time.  Works like a charm.  If I then
#include a file that is one of the pre-cooked ones in dump file, the
compiler doesn't include it again.  This cuts compile times way down.
_____        
  |      Todd M. Lewis            Disclaimer: If you want my employer's
  ||\/|  utoddl@ecsvax.uncecs.edu             ideas, you'll have to
  ||  || utoddl@ecsvax.bitnet                 _buy_ them. 
   |  ||     
       |___   ("Prgrms wtht cmmnts r lk sntncs wtht vwls." --TML)

henry@utzoo.uucp (Henry Spencer) (10/25/89)

In article <11396@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:
>>      I propose a solution via compiler optimization.
>
>Your solution does not at all seem to me to preserve existing semantics.

Can you elaborate on this, Doug?  Seems to me like what he was proposing --
have compiler recognize files bracketed with `#ifndef FOO_H' and remember
the bracketing -- comes under the "as if" rule.  Re-including such a file
with FOO_H defined cannot possibly have any effect except to slow down
the compilation.
-- 
A bit of tolerance is worth a  |     Henry Spencer at U of Toronto Zoology
megabyte of flaming.           | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

dpi@loft386.UUCP (Doug Ingraham) (10/26/89)

In article <4643@buengc.BU.EDU>, bph@buengc.BU.EDU (Blair P. Houghton) writes:
> How about ( in <foo.h>):
> 
> 	#pragma Never_Again
> 
> Which tells the BlairTech/ANSI1.0 compiler that when it hits
> a #include to read this file again, it should just ignore it.
> 
> 				--Blair

In GNU gcc this is already implemented as a pragma.

#pragma once

Seems like an obvious enough thing that every compiler vendor
will eventually have their own incompatible version of this
construct.

I have put both constructs in my ANSI compatible header files so
that they look like this:

#pragma once
#ifndef _STRING_H
#define _STRING_H

/* REST OF HEADER */

#endif

In this way if compiled on a compiler that doesn't recognize the once
pragma, it still has the desired effect.

-- 
Doug Ingraham (SysAdmin)
Lofty Pursuits (Public Access for Rapid City SD USA)
uunet!loft386!dpi

bright@Data-IO.COM (Walter Bright) (10/26/89)

In article <CIMSHOP!DAVIDM.89Oct24095658@uunet.UU.NET> cimshop!davidm@uunet.UU.NET (David S. Masterson) writes:
>In article <14240@well.UUCP> nagle@well.UUCP (John Nagle) writes:
<   (Many newer compilers do "#" processing in the same pass as main
<   compilation, so referring to a "preprocessor" in this context is not
<   necessarily correct.)
<I suppose that this is a performance enhancement and that now "#" processing
<is being considered so much a part of the language that compiler writers feel
<justified in doing this.  However, doesn't this limit flexibility?

Integrating the preprocessor into the parser is a big performance win
because you save a file write and read of the source. Loading and starting
one program is also faster than loading and starting two. The MS-DOS
compiler market is such that an integrated preprocessor is pretty much
required.

An integrated preprocessor has an advantage usually because it knows about
types and typedefs, so sizeof expressions can be used in #if expressions
(though ANSI C disallows it).

What you lose is the ability to run the preprocessor on non-C source files.
Note that integrating the preprocessor and the parser does not prevent
you from viewing the preprocessed output, there is usually a switch to
generate this.

peter@ficc.uu.net (Peter da Silva) (10/26/89)

In article <2185@dataio.Data-IO.COM> bright@dataio.Data-IO.COM (Walter Bright) writes:
> What you lose is the ability to run the preprocessor on non-C source files.

Well, that writes off Imakefile. Oh well, X on an IBM is a lost cause.
-- 
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
"That particular mistake will not be repeated.  There are plenty of        'U`
 mistakes left that have not yet been used." -- Andy Tanenbaum (ast@cs.vu.nl)

allbery@NCoast.ORG (Brandon S. Allbery) (10/26/89)

As quoted from <14790@bfmny0.UU.NET> by tneff@bfmny0.UU.NET (Tom Neff):
+---------------
| In article <1659@atanasoff.cs.iastate.edu> hascall@atanasoff.UUCP (John Hascall) writes:
| >    Since the impending ANSI standard requires that including a file more
| >    than once have exactly the same effect as including it once...why can't
| >    a compiler just ignore #includes for files it has already #included???
| 
| Including standard HEADER files should be idempotent.  Back here in the
| real world there are plenty of uses for including a file multiple times
| with a desired substantial effect on each inclusion.  Examples include
+---------------

Agreed.  I have simulated packages this way, e.g.

	#define PACKAGE_NAME foo
	#define PACKAGE_TYPE float
	#include "package.h"

++Brandon
-- 
Brandon S. Allbery:  allbery@NCoast.ORG, BALLBERY (MCI Mail), ALLBERY (Delphi)
uunet!hal.cwru.edu!ncoast!allbery ncoast!allbery@hal.cwru.edu bsa@telotech.uucp
*(comp.sources.misc mail to comp-sources-misc[-request]@backbone.site, please)*
*Third party vote-collection service: send mail to allbery@uunet.uu.net (ONLY)*
>>>	 Shall we try for comp.protocols.tcp-ip.eniac next, Richard?	    <<<

dave@charyb.COM (Dave Rifkind) (10/26/89)

Just out of interest, would something like this do the job?

     #pragma abandon _FILENAME_H
     #ifndef _FILENAME_H
     #define _FILENAME_H
     ...
     #endif /* _FILENAME_H */

...where "#pragma abandon <name>" means "immediately terminate
processing this file if <name> is #defined"?

mustard@sdrc.UUCP (Sandy Mustard) (10/26/89)

In article <1088@odin.SGI.COM>, shap@delrey.sgi.com (Jonathan Shapiro) writes:
> 
> Okay, here's another cute trick.
> 
> inside FRED_H do the following:
> 
>    #ifdef FRED_H
>    #undef FRED_H
>    #define FRED_H /dev/null
>    #endif
> 
But it only works on those systems that have /dev/null. It is not PORTABLE!

Sandy Mustard

nagle@well.UUCP (John Nagle) (10/27/89)

In article <1989Oct24.060920.28655@cs.rochester.edu> ken@cs.rochester.edu writes:
->But is this really as inefficient as people think? I tried the
->following on a Sun-4/60
->
->% wc grammar0.cc
->     932    2944   19700 grammar0.cc
->% g++ -I../h -E grammar0.cc | wc
->    3728    8219   63497
->% time g++ -I../h -E grammar0.cc > /tmp/foo.cc
->0.4u 0.3s 0:01 44% 0+208k 0+9io 0pf+0w
->
->Looks pretty insignificant compared to parsing and CG time.

       What are you comparing to what?  Only one time measurement is given.
This makes it rather meaningless to draw any conclusions.


					John Nagle

decot@hpisod2.HP.COM (Dave Decot) (10/27/89)

> Why does everybody feel compelled to reinvent this wheel?

Because there's no STANDARD (**HINT**) way to do it that has any
hope of efficiency!!

Dave

peter@ficc.uu.net (Peter da Silva) (10/27/89)

How about just "#pragma exit"? This would terminate reading the current
file immediately.

#ifdef HEADER_H
#exit
#endif
#define HEADER_H
...
-- 
`-_-' Peter da Silva <peter@ficc.uu.net> <peter@sugar.hackercorp.com>.
 'U`  --------------  +1 713 274 5180.
"That particular mistake will not be repeated.  There are plenty of mistakes
 left that have not yet been used." -- Andy Tanenbaum (ast@cs.vu.nl)

diamond@csl.sony.co.jp (Norman Diamond) (10/27/89)

Someone:

>>>      I propose a solution via compiler optimization.

In article <11396@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn) writes:

>>Your solution does not at all seem to me to preserve existing semantics.

In article <1989Oct25.164145.29980@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:

>Can you elaborate on this, Doug?  Seems to me like what he was proposing --
>have compiler recognize files bracketed with `#ifndef FOO_H' and remember
>the bracketing -- comes under the "as if" rule.  Re-including such a file
>with FOO_H defined cannot possibly have any effect except to slow down
>the compilation.

I mostly agree with Mr. Spencer.  However, perhaps we need an additional
interpretation ahead of this one.  Is inclusion a "volatile" operation?

Hmm.  In fact, if a program is being interpreted instead of compiled,
the program itself can be responsible for its include files being
changed between two successive inclusions.  Now maybe C will finally
replace LISP.  :-) :-)

-- 
Norman Diamond, Sony Corp. (diamond%ws.sony.junet@uunet.uu.net seems to work)
  Should the preceding opinions be caught or     |  James Bond asked his
  killed, the sender will disavow all knowledge  |  ATT rep for a source
  of their activities or whereabouts.            |  licence to "kill".

fox@allegra.att.com (David Fox) (10/27/89)

A problem with the idea of having the compiler recognize the

	#ifndef FOO
	#define FOO
	...
	#endif

arrangement (I wouldn't call it a construct) is what if the
including file explicitly #undefs FOO?  Then the semantics are
not preserved.  A somewhat unlikely occurence, I'll admit.

A problem I've run into with the construct itself (with a
standard preprocessor) is that I often create a new header
file by copying an old one.  You can guess what happens:
for some strange reason the compiler doesn't recognize the
new type name.

My real objection is that I don't like having to type three
lines in order to say one thing.  I don't go to great lengths
to reuse code rather than rewrite it just to sprinkle my code
liberally with obscure three line constructs that just mean
"not again!"

David Fox


--
+-----------------------------------------+ David Fox
|End tax incentives for leveraged buyouts!| fox@allegra.att.com
+-----------------------------------------+ (presumably)

ken@cs.rochester.edu (Ken Yap) (10/27/89)

|->But is this really as inefficient as people think? I tried the
|->following on a Sun-4/60
|->
|->% wc grammar0.cc
|->     932    2944   19700 grammar0.cc
|->% g++ -I../h -E grammar0.cc | wc
|->    3728    8219   63497
|->% time g++ -I../h -E grammar0.cc > /tmp/foo.cc
|->0.4u 0.3s 0:01 44% 0+208k 0+9io 0pf+0w
|->
|->Looks pretty insignificant compared to parsing and CG time.
|
|       What are you comparing to what?  Only one time measurement is given.
|This makes it rather meaningless to draw any conclusions.

Sorry, sloppy of me:

% time g++ -I../h -c grammar0.cc
10.5u 2.5s 0:22 57% 0+1532k 34+37io 202pf+0w

5% of the total time is not something I care to worry about at this time.

jgh@root.co.uk (Jeremy G Harris) (10/27/89)

In article <2185@dataio.Data-IO.COM> bright@dataio.Data-IO.COM (Walter Bright) writes:
>Integrating the preprocessor into the parser is a big performance win
>because you save a file write and read of the source.

Except on a multiprocessor system.  Assuming decent comms bandwidth between
processors, it should make sense to split the compiler up fairly finely and
pipeline the work.

I know that more modern 'make' variants help somewhat, but I'd like to give
those sixteen processors in our Sequent real work to do even when I'm only
compiling one or two files.

So, has anyone done this, and measured the results?  We want information!

-- 
Jeremy Harris			jgh@root.co.uk

ok@cs.mu.oz.au (Richard O'Keefe) (10/27/89)

In article <2550110@hpisod2.HP.COM>, decot@hpisod2.HP.COM (Dave Decot) writes:
> > Why does everybody feel compelled to reinvent this wheel?
> Because there's no STANDARD (**HINT**) way to do it that has any
> hope of efficiency!!

But we've been shown several.  The simplest is the two-files-per-header
method:
	your file		foo.h			foo1.h

	#include "foo.h"	#ifndef FOO_H		/* whatever */
				#define FOO_H 1
				#include "foo1.h"
				#endif

You can apply this technique even when foo1.h is a header file supplied
by someone else:  foo.h gets rescanned every time but this is file
open + file close + reading four lines, and the #pragma once hack saves
you just the cost of reading three lines.  Big saving, huh?

henry@utzoo.uucp (Henry Spencer) (10/27/89)

In article <FOX.89Oct26215145@novax.allegra.att.com> fox@allegra.att.com (David Fox) writes:
>A problem with the idea of having the compiler recognize the
>
>	#ifndef FOO
>	#define FOO
>	...
>	#endif
>
>arrangement (I wouldn't call it a construct) is what if the
>including file explicitly #undefs FOO? ...

Then the compiler notices that the magic symbol associated with the file
has been messed with, and suppresses the optimization.  Really, it can
be done, and it's not even hard.
-- 
A bit of tolerance is worth a  |     Henry Spencer at U of Toronto Zoology
megabyte of flaming.           | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/28/89)

In article <8956@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
-))In article <14240@well.UUCP) nagle@well.UUCP (John Nagle) writes:
-)))	#ifndef XXX
-)))	#define XXX
-)))	...content...
-)))	#endif
-... do it this way...
-	#ifndef XXX
-	...content...
-	#define XXX
-	#endif

I recommend just the opposite.  If the body of the header #includes
itself recursively (which is a very useful thing to permit), then
the idempotency lock doesn't work right unless you set it before
the nested #inclusion.

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/28/89)

In article <1989Oct25.164145.29980@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
-Can you elaborate on this, Doug?  Seems to me like what he was proposing --
-have compiler recognize files bracketed with `#ifndef FOO_H' and remember
-the bracketing -- comes under the "as if" rule.  Re-including such a file
-with FOO_H defined cannot possibly have any effect except to slow down
-the compilation.

I received some private correspondence on this also, and apparently I
didn't grasp the actual meat of the proposal.

I suppose if further #undef/#define of the identifier were properly
tracked, it would work.

karl@haddock.ima.isc.com (Karl Heuer) (10/28/89)

In article <FOX.89Oct26215145@novax.allegra.att.com> fox@allegra.att.com (David Fox) writes:
>A problem with the idea of having the compiler recognize the
>	#ifndef FOO  #define FOO  ... #endif
>arrangement (I wouldn't call it a construct) is what if the
>including file explicitly #undefs FOO?

The optimization is that the compiler, on first encountering a header H that
fits the pattern `#if EXPR ... #endif', stores the pair <H, EXPR> in an
internal table.  (In your example, the expression is `!defined(FOO)'.)  Any
request to include a file F first checks the table to see if F appears as the
left side of some pair <F, EXPR>, and if so, interprets the request as if it
had read `#if EXPR   #include F   #endif'.  So if the user has disabled the
guard expression, the file will indeed be included a second time, as desired.

(Note also that it's not necessary for the compiler to be able to determine
that two names denote the same file.  If it fails to detect this, then the
optimization doesn't take place, but the semantics are preserved.)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

dhesi@sun505.UUCP (Rahul Dhesi) (10/28/89)

In article <1088@odin.SGI.COM> shap@delrey.sgi.com (Jonathan Shapiro)
writes:

>   #include FRED_H

Please be alert for problems.  K&R requires the token after the
"#include" to be a filename enclosed in double quotes or angle
brackets, not an arbitrary symbol.  It was not until the ANSI C
standard that the generalized syntax was blessed.

Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com>
UUCP:  oliveb!cirrusl!dhesi
Use above addresses--email sent here via Sun.com will probably bounce.

marc@dumbcat.UUCP (Marco S Hyman) (10/29/89)

In article <1011@cirrusl.UUCP> dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes:
    In article <1088@odin.SGI.COM> shap@delrey.sgi.com (Jonathan Shapiro)
    writes:
    
    >   #include FRED_H
    
    Please be alert for problems.  K&R requires the token after the
    "#include" to be a filename enclosed in double quotes or angle
    brackets, not an arbitrary symbol.

And the C compiler shipped with System V/386 3.2 (ISC's flavor) coffs
(I couldn't help myself ;-) on that one -- At least it didn't like
#include __FILE__.  (Another reason to use gcc/g++).

--marc
-- 
// Marco S. Hyman		{ames,pyramid,sun}!pacbell!dumbcat!marc

bill@twwells.com (T. William Wells) (10/30/89)

In article <610@wang.UUCP> mds@wang.UUCP (Marc San Soucie) writes:
: This is not an insignificant issue when file opens are slow, such as across
: some networked file systems. Admittedly compiling across a network is kind of
: goofy, but mature systems shouldn't impose such restrictions on their users.

Well, I don't know.... When I compile programs at work, I haven't
the faintest idea which computer my files happen to be on, unless
I go to some effort to check.

Last time I checked, I log in on proxftl, but my home directory
was on prox04, and my project directories were on prox03.

Oh, and the way we deal with the include problem is this, as has
already been suggested:

In foo.h:

	#ifndef FOO_H
	#define FOO_H
	... stuff in foo.h
	#endif /* FOO_H */

In any include file including foo.h:

	#ifndef FOO_H
	#include "foo.h"
	#endif

We don't bother with wrapping include files in .c files, since
this just makes things harder to read without adding that much
improvement.

---
Bill                    { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com

dhesi@sunscreen.UUCP (Rahul Dhesi) (10/30/89)

In article <1087@odin.SGI.COM> shap@delrey.sgi.com (Jonathan Shapiro) writes:
>The current most widley accepted solution for single inclusion is to
>insert a pragma into the header file:
>
>   #pragma once

A serious mistake, because this pragma can affect the meaning of a
program, and therefore cannot be safely ignored.

Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com>
UUCP:  oliveb!cirrusl!dhesi
Use above addresses--email sent here via Sun.com will probably bounce.