[comp.lang.c++] Including header files minimally.

dld@f.gp.cs.cmu.edu (David Detlefs) (11/12/88)

There have been a number of interesting suggestions on how to go about
including header files only as needed.  Someone suggested including
them using something like an 

#include_once

directive.  It seems to me that the "include-once-ability" of a header
file ought to be a property of the file, independent of the context in
which is included.  This would favor putting something in the file
itself.  The "#ifndef/#define/<body>/#endif" style, while not pretty, is
certainly sufficient and requires no modifications to cpp.  Every so
often, someone writes an include file that is really meant to be
included multiple times -- it contains #ifdef's that depend an symbols
whose values are changed between inclusions.  Files of this kind could
not be include-once.  If we did want to modify cpp, though, we could
take care of both situations minimally by recording as we process the
inclusion of a file all the preprocessor symbols used in #ifdef's and
the values they had at the time of this inclusion.  We would store
this list in a table, and when we next included the file, check the
current values of those symbols.  If they are the same as before,
don't include the file, otherwise do.

It would be nice if you could precompile each header file to get a
symbol table (both preprocessor and language tables -- this scheme
would require an integrated preprocessor/compiler) that you would dump
to a file.  Maybe you would call this a ".hx" file.  Whenever you
include a .h file, check for a corresponding .hx file in the same
directory.  If you find it, look at the .hx file; in there, you've
recorded this same list of external preprocessor symbols it depends on
and the values for those that were used when this was compiled.  If
they agree with the current values for those symbols, then you just
read in the file.  (I have some ideas about how to dump/undump the
symbol table, but that's another discussion.)

I think we really need something like this to get decent compilation
performance.  The project I work on requires all programs written in
the the language we're writing to include a file that expands to
upwards of 3500 lines of code.  Compiles take on the order of 5
minutes on an IBM RT/PC.  Not very good.  I'd like to see if some
scheme like the above could make this better.

-- 
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.)
-- 

jim@athsys.uucp (Jim Becker) (11/15/88)

From article <3561@pt.cs.cmu.edu>, by dld@f.gp.cs.cmu.edu (David Detlefs):
>
	[stuff deleted]
> 
> It would be nice if you could precompile each header file to get a
> symbol table (both preprocessor and language tables -- this scheme
> would require an integrated preprocessor/compiler) that you would dump
> to a file. [clipped]
> 

	Something fairly equivalent to this existed in the Amiga
world, and it's net effect was excellent. I found that using this type
of approach made development on a 68000/floppy based Amiga faster than
a 80286/harddisk system! This was part of the Manx development system
(aka - Aztec C).

	The implementation consisted of having a special option for
the compiler that would take the symbol table that it currently had
and outputing the content as a "library" file. Not a standard type
library, but a library of symbols that were known. This creation I did
using a C file that contained #include statements for all the standard
include files that I used in the system. They were then bundled into
this small binary format library.

	During subsequent compilation, the created symbol library file
was included in the command line. Any include files that were needed
from during the compilation process were semantically extracted from
the symbol library when they were needed. Include files that were not
in the symbol library were processed normally.

	The result was a DRAMATIC improvement in the compilation
speed! I have been surprised that this concept hasn't yet come into
other products that I have crossed paths with, as it seems very
elegant and nice (w/ not much effort).

	We just received the Oregon C++ compiler, I hope they may
integrate something such as this into their compiler to help further
improve the speed of compilation!!


> 
> -- 
> Dave Detlefs		
> Carnegie-Mellon CS	


-Jim Becker

ok@quintus.uucp (Richard A. O'Keefe) (11/15/88)

In article <186@tityus.UUCP> jim@athsys.uucp (Jim Becker) describes
>a special option for
>the compiler that would take the symbol table that it currently had
>and outputing the content as a "library" file.

>I have been surprised that this concept hasn't yet come into
>other products that I have crossed paths with, as it seems very
>elegant and nice (w/ not much effort).

That's an _old_ technique!  Burroughs were using it _years_ ago on
their mainframe Algol, Fortran, COBOL, &c compilers.  It has been
described several times in the technical literature.  And of course
Simula 67 was doing fully type-checked separate compilation with
symbol-table files before C (plain C) was dreamed of.

jim@athsys.uucp (Jim Becker) (11/17/88)

From article <681@quintus.UUCP>, by ok@quintus.uucp (Richard A. O'Keefe):
> In article <186@tityus.UUCP> jim@athsys.uucp (Jim Becker) describes
>>a special option for
>>the compiler that would take the symbol table that it currently had
>>and outputing the content as a "library" file.
> 
>>I have been surprised that this concept hasn't yet come into
>>other products that I have crossed paths with, as it seems very
>>elegant and nice (w/ not much effort).
> 
> That's an _old_ technique!  Burroughs were using it _years_ ago on
> their mainframe Algol, Fortran, COBOL, &c compilers.  It has been
> described several times in the technical literature.  And of course
> Simula 67 was doing fully type-checked separate compilation with
> symbol-table files before C (plain C) was dreamed of.


	Ok -- I guess that it's been around for a long time, I haven't
worked with any Burroughs equipment and hadn't seen it w/ the machines
that I have worked on.

	If this is such a time honored technique, the next question is
"Why isn't it used everywhere?". Like I said, my experience has only
seen it implemented on the Amiga, but I have worked will just about
all the DEC line, Data General line (thru 83), Sun and others. Is there
some legal problem with people putting it into the compiler? This is
a fairly simple, and I guess widely used in some circles, technique
that could greatly aid this include dependency problem of C++. Wny 
isn't this already part of the environment?

-Jim Becker

rfg@nsc.nsc.com (Ron Guilmette) (11/17/88)

In article <3561@pt.cs.cmu.edu> dld@f.gp.cs.cmu.edu (David Detlefs) writes:
>There have been a number of interesting suggestions on how to go about
>including header files only as needed...
>...  It seems to me that the "include-once-ability" of a header
>file ought to be a property of the file, independent of the context...
>...Every so often, someone writes an include file that is really meant to be
>included multiple times...

Ever heard of unix links?

>...If we did want to modify cpp, though, we could
>take care of both situations minimally by recording as we process the
>inclusion of a file all the preprocessor symbols used in #ifdef's and
>the values they had at the time of this inclusion.  We would store
>this list in a table, and when we next included the file, check the
>current values of those symbols.  If they are the same as before,
>don't include the file, otherwise do.

I think that there is semantic muddy-ness to the term "at the time of
this inclusion".  When is that exactly?  At the *start* of the inclusion,
somewhere in the middle, or at the end?  Keep in mind that the defined
values may actually change as a result of the inclusion itself.

My idea was far simpler and far less expensive in compile time.  I
suggested (and still suggest) that cpp could be taught to remember a
list of "already included" filenames.  It could then ignore additional
#include's for any file in this list.  You could easily override this
simple logic by making multiple links to individual header files which
must be included multiple times.

>It would be nice if you could precompile each header file to get a
>symbol table (both preprocessor and language tables -- this scheme
>would require an integrated preprocessor/compiler) ...

I think that you may want to switch over to comp.lang.ada ;-)

>I think we really need something like this to get decent compilation
>performance.

Yes.  That is an issue isn't it.  All the more reason to think about
my suggestion.

-- 
Ron Guilmette
National SemiConductor, 1135 Kern Ave. M/S 7C-266; Sunnyvale, CA 94086
Internet: rfg@nsc.nsc.com   or   amdahl!nsc!rfg@ames.arc.nasa.gov
Uucp: ...{pyramid,sun,amdahl,apple}!nsc!rfg

dld@f.gp.cs.cmu.edu (David Detlefs) (11/19/88)

To Ron Guilmette -- I couldn't seem to send you mail --

I think your idea is a very good quick-and-dirty solution -- easy to
understand, easy to implement.  If someone were to take the sources to
some public domain cpp and implement it tomorrow (which is about all it
would take!), it would get a lot of use.  People *shouldn't* write
include files that are intended to be included multiple times with
different results on each inclusion; that is an obscure practice at
best.  Yet people do do this; at least they have, in include files that
we may have to keep including forever in the name of backward
compatibility.  Your solution, with the links, works.  However, I would
hope that you would agree that it is a least somewhat unsatisfying.

I think your solution and mine fall on a continuum; yours is very easy
to implement, but requires the user to perform two actions he or she
wouldn't normally have to do: use the new flag to cpp (or use the flag
to turn it off when necessary, if not including multiply is made the
default), and making extra links to certain files.  Note that you run
the risk of always using the "don't include again mode," and forgetting
to turn it off when you include a file from an external library that
includes a file that includes file x.h 7 times, each time with a
different preprocessor environment.

My solution requires somewhat more work, but in my opinion, not too
much, and solves all problems, without, I think, too much cost in
performance.  I don't think it is "semantically muddy."  To explain it
again: you include a file.  If this file contains no #if-like
constructs, it will generate the same code every time it is included,
so it will never need to be included again.  If it does have an
#if-like construct, then it may generate different code if it is
included again at a time when the preprocessor symbols that are used in
the #if have different values.  (Obviously, if we #define one of these
symbols before using it, it doesn't depend on the external value.)
While we process the #include file for the first time, we record all
the externally defined symbols uses in #if's, and their values when
they were first used.  If we encounter an #include of this file again,
we include it only if one or more of those symbols has a different
value.

This solution requires no special flags, and always gets it right.
Your solution has the drawbacks I mentioned, but has the virtue of
being simpler to implement, and may be somewhat faster.  Both, I think,
are valid ideas.

Trying to keep the intellectual discourse friendly...


-- 
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.)
-- 

gsf@ulysses.homer.nj.att.com (Glenn Fowler[eww]) (11/19/88)

In article <3614@pt.cs.cmu.edu>, dld@f.gp.cs.cmu.edu (David Detlefs) writes:
> ...
> performance.  I don't think it is "semantically muddy."  To explain it
> again: you include a file.  If this file contains no #if-like
> constructs, it will generate the same code every time it is included,
> ...

this breaks with the following simple example:
x.h:
	extern int	VARIABLE;
tst.c:
	#include "x.h"
	#define VARIABLE	abc
	#include "x.h"

a proper implementation would have to keep a list of all identifiers
referenced within a header to determine if a repeated include would
produce different results -- the problem is compounded by nested
includes
-- 
Glenn Fowler    (201)-582-2195    AT&T Bell Laboratories, Murray Hill, NJ
uucp: {att,decvax,ucbvax}!ulysses!gsf       internet: gsf@ulysses.att.com

campbell@redsox.UUCP (Larry Campbell) (11/21/88)

In article <3614@pt.cs.cmu.edu> dld@f.gp.cs.cmu.edu (David Detlefs) writes:

}                                     ...  People *shouldn't* write
}include files that are intended to be included multiple times with
}different results on each inclusion; that is an obscure practice at
}best.

I must disagree.  We regularly use the following idiom, which I think
is quite defensible, for managing user and error message definitions in
our products:

Messages are defined in a header file that looks like this:

------------------------------message.h------------------------------
#ifdef GenerateTable
#define MSG(name, string) char name[] = string;
#else
#define MSG(name, string) extern char name[];
#endif

MSG(err_you_lose, "you lose big time, pal")
MSG(err_get_real, "get real, pal")
MSG(err_swap_mad, "swap read error, you lose your mind")
---------------------------end of message.h--------------------------

Modules that refer to message definitions just include message.h.
One very small module then defines the messages:

------------------------------message.c------------------------------
#define GenerateTable
#include "message.h"
---------------------------end of message.c--------------------------

We can then produce foreign language versions of our products more easily
because all the messages are confined to one file, and we can also easily
produce an appendix for the documentation that lists all possible error
messages.
-- 
Larry Campbell                          The Boston Software Works, Inc.
campbell@bsw.com                        120 Fulton Street
wjh12!redsox!campbell                   Boston, MA 02146

tuck@alanine.cs.unc.edu (Russ Tuck) (11/21/88)

In article <3614@pt.cs.cmu.edu> dld@f.gp.cs.cmu.edu (David Detlefs) writes:
>...My solution ... To explain it
>again: you include a file.  If this file contains no #if-like
>constructs, it will generate the same code every time it is included,
>so it will never need to be included again.  

This isn't true.  An included file may generate different code if any
preprocessor definitions have changed or been added.  As a trivial example,
consider a triv.h file containing only "int fn();", and the following fragment:

#include "triv.h"
#define int unsigned long
#include "triv.h"

Without a good reason and documentation, this is certainly highly questionable
code, but it shows that *any* identifier or keyword can be redefined.  You 
can't be sure that an include file will generate the same code twice unless 
the preprocessor definitions have not changed at all (and even this is 
probably not sufficient to make a guarantee).

Let me give a motivating example for code like this.  I am implementing a 
hierarchy of mathematical types, derived from a single base, and need to 
provide math and logic expressions for all these classes.  Unfortunately, 
just providing all the operator methods for the base class is not sufficient.
The base class methods return base class values, which can not be assigned 
to derived class objects (because the additional info contained in the 
derived class was lost when the operator method converted the result to 
the base class).  So, I have to redefine all the operator methods for each 
derived class.  To avoid many potential typographic and minor logic errors, 
I do this with a single include file with declarations something like:
 
type operator+(type)

Then the library user includes a single library definition .h file, which
includes the above file multiple times, something like this:

#define type base
#include "my_math_defs.h"
#define type derived
#include "my_math_defs.h"

(This is greatly simplified from the real code, but gives a feel for one
case where a .h file can usefully generate different code without #if's.)
 
>...While we process the #include file for the first time, we record all
>the externally defined symbols uses in #if's, and their values when
>they were first used.  If we encounter an #include of this file again,
>we include it only if one or more of those symbols has a different
>value.
>
>This solution requires no special flags, and always gets it right.

No. It's not this simple.  For complete safety, it must be included again
if anything about the set of preprocessor definitions has changed (or perhaps
even if the #include just comes in a different code context).

Russ Tuck		               internet: tuck@cs.unc.edu
Computer Science Dept., Sitterson Hall csnet:    tuck@unc
University of North Carolina           uucp:     {ihnp4|decvax}!mcnc!unc!tuck
Chapel Hill, NC 27599-3175, USA        Phone:    (919) 962-1755 or 962-1932

dld@f.gp.cs.cmu.edu (David Detlefs) (11/22/88)

I got a couple of interesting responses to my last post on this
subject, on which I'd like to comment.  First, Glenn Fowler quite
correctly points out that the simple example

>x.h:
>        extern int      VARIABLE;
>tst.c:
>        #include "x.h"
>        #define VARIABLE        abc
>        #include "x.h"

breaks my scheme (which was, briefly, to record what preprocessor
variables an include file depends on, and their values at the point of
first inclusion, and then only reinclude if those values have
changed.)  Glenn's point is that there is no way to determine what
identifiers are preprocessor variables the first time through; any
random #define may make what was a perfectly good identifier into a macro.
I see no way to save my plan from this fatal flaw, and hereby abandon
it publicly (insert "Taps" here...)  (These kind of sick examples point
out the essential corruption of cpp 1/2 :-)  I abandon it to the
extent that it was supposed to do everything right always while requiring no
new semantics and minimal performance cost.  It still may be a useful
heuristic, as stated, to compete with the "insert an #include_once
declaration in the header file (or in the includer)" or "tell cpp to
just include once."

A final note: Larry Campbell protests my classification of the
practice of including files multiple times in the same program as
"obscure at best," putting forth the following example:

>------------------------------message.h------------------------------
>#ifdef GenerateTable
>#define MSG(name, string) char name[] = string;
>#else
>#define MSG(name, string) extern char name[];
>#endif
>
>MSG(err_you_lose, "you lose big time, pal")
>MSG(err_get_real, "get real, pal")
>MSG(err_swap_mad, "swap read error, you lose your mind")
>---------------------------end of message.h--------------------------

I don't think this is bad usage; I probably didn't make it clear that
the practice I thought was bad was the inclusion of the same file
multiple times in the same *compilation unit* with different intended
results.  I assume that you would only need to include message.h once
per .c file, with GenerateTable either undefined in all but one of the
.c files.  Fine, no problem; what I would have object to would be
doing this using something like


------------------------------message1.h-----------------------------
#ifdef GenerateTable
#define MSG(name) char name[] = MSG_STRING;
#else
#define MSG(name) extern char name[];
#endif
---------------------------end of message1.h--------------------------
------------------------------message.h------------------------------
#define MSG_STRING "you lose big time, pal" 
#include <message1.h>
MSG(err_you_lose)

#define MSG_STRING "get real, pal"
#include <message1.h>
MSG(err_get_real)

#define MSG_STRING "swap read error, you lose your mind"
#include <message1.h>
MSG(err_swap_mad)
---------------------------end of message.h--------------------------

I trust you will agree that this is pretty obscure (at best)!

-- 
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.)
-- 

prh@actnyc.UUCP (Paul R. Haas) (11/22/88)

In article <10873@ulysses.homer.nj.att.com> gsf@ulysses.homer.nj.att.com (Glenn Fowler[eww]) writes:
>In article <3614@pt.cs.cmu.edu>, dld@f.gp.cs.cmu.edu (David Detlefs) writes:
>> ...
>> [David discusses his scheme for avoiding including files to often.]
>> ...
>
> [Glenn shows a flaw with David's method.]

I think I have a solution which is ANSI compatible and requires minimal
source code changes (some people write their header files this way already).

Make the C preprocessor recognize files of the form:
	comments and whitespace
	#ifndef SOME_SYMBOL
	any properly nested stuff, hopefully including "#define SOME_SYMBOL"
	#end
	comments and whitespace
And only include them a second time if the symbol is not defined.  The
preprocessor would have to keep a table of include file names and
symbols that they depend on.  Obviously, this should be turned off if
the preprocessor is sending comments through.

Advantages:
  o Code will behave the same way with preprocessors without this feature.
  o No #pragmas required
  o Easy to explain to people
Disadvantages:
  o In order for this to speed things up, most header files should be
    written in a particular form.
  o I am not sure how much effort this will take.  It looks like I will
    end up putting some ugly hacks into cccp.c.
  o Will slightly slow down the preprocessor for header files which lack
    the #ifndef ... #endif structure.

Feel free to point out something that I missed.  I am going to look into
adding this to the Gnu preprocessor, cccp.  If anyone else is doing something
similar, let me know.
-----
Paul Haas	uunet!actnyc!prh or prh@frith.egr.msu.edu
(212) 696-3653

rfg@nsc.nsc.com (Ron Guilmette) (11/24/88)

In article <3614@pt.cs.cmu.edu> dld@f.gp.cs.cmu.edu (David Detlefs) writes:
>To Ron Guilmette -- I couldn't seem to send you mail --

Several people have told me this.  I don't know hat's happening but I
intend to find out!

>I think your idea is a very good quick-and-dirty solution -- easy to
>understand, easy to implement.  If someone were to take the sources to
>some public domain cpp and implement it tomorrow (which is about all it
>would take!), it would get a lot of use.

I agree that what I proposed is "quick" I disagree that it is "dirty".
I'll see if I can find the time to make the necessary mods to GNU cpp soon.
If I do get it done, I will post the necessary patches here and in the
GNU C and GNU C++ newsgroups.  The diffs ought to be reasonably small.

>People *shouldn't* write
>include files that are intended to be included multiple times with
>different results on each inclusion; that is an obscure practice at
>best.  Yet people do do this; at least they have, in include files that
>we may have to keep including forever in the name of backward
>compatibility.

I generally agree that this seems to be a questionable practice, but I'd
rather be a bit less judgemental about it.  If people have found what they
think are good reasons for doing this, then, to quote the Beatles, "Let it be".

>Your solution, with the links, works.  However, I would
>hope that you would agree that it is a least somewhat unsatisfying.

No I don't agree.  It tastes great!  No.  It's less filling!  Tastes great!
Less filling! ...

Well, OK.  It is a little kludgy, but then all of cpp is just a big kludge
which people keep on using year after year because it does some things very
well which cannot yet be done in any other ways.

>I think your solution and mine fall on a continuum...

That sounds painful.  I would rather fall on my sword that on a continuum!

>... yours is very easy
>to implement, but requires the user to perform two actions he or she
>wouldn't normally have to do: use the new flag to cpp (or use the flag
>to turn it off when necessary, if not including multiply is made the
>default), and making extra links to certain files.  Note that you run
>the risk of always using the "don't include again mode," and forgetting
>to turn it off...

OK.  The problems you point out are very valid.  Looks like it is time
for me to switch to plan B.

In retrospect, I admit that the notion of an extra flag to cpp (i.e. the
don't do multiple includes flag) was a dumb idea.  As you have noted, it
lacks true backward compatibility in the purest sense (i.e. not even having
to edit your Makefiles).  The multiple links to get multiple inclusions of
the same single file was also a dumb idea.

Plan B:

So let's fix cpp to do the following.  First, under "normal" circumstances,
the new cpp will do just what it has traditionally done, i.e. include EACH
and EVERY file called for in EACH and EVERY #include directive.  Now for
the trick.  Pick one of the UNIX protection mode bits which is typically
NEVER USED and totally INSIGNIFICANT (as least for normal source code files).
Just for the sake of argument, lets pick the "set-gid" bit (i.e. 02000).
Now let's say that we modify cpp so that it will *NOT* re-include any header
file which has its set-gid bit set.  Ta da!  Presto!  No more tricky links,
full backwards compatibility, and best of all, the ability to control
re-inclusions on a file-by-file basis in a very easy and simple manner
which does *not* require any changes to Makefiles or any other files.

The above scheme is simple to implement, simple to use, and simple to
understand.  The semantics are crystal clear and it can be slowly worked
into existing code (both C and C++) on an as-needed basis.

Although it would seem that this scheme is heavily dependent on UNIX-specific
file modes, I believe that most operating systems have at least some ways
of tagging individual files with one more meaningful bit of information.
Specifically, MS-DOS also has a per-file mode, and I believe that VMS does
also.

>My solution requires somewhat more work...
>...I don't think it is "semantically muddy."...

Is my suggested approach "muddy"?  I don't see how?

>again: you include a file.  If this file contains no #if-like
>constructs, it will generate the same ***code*** every time it is included,
>so it will never need to be included again.

It is *NOT* true that, if there are no #if's in a given header file, then
you will get the same "code" (let alone the same "effect") from each inclusion
of that file.  Note the following:

	/* something.h  */

	int my_array[FOOBAR];

There are no #if's in this file, but the code can still be different on
different inclusions if the value of FOOBAR changes.

More significant is that the "effect" of a header file (without any #if's)
can be different for different inclusions, i.e.:

	#define NEW_FOOBAR	FOO##BAR

>[My] solution requires no special [cpp] flags, and always gets it right.
>Your solution has the drawbacks I mentioned, but has the virtue of
>being simpler to implement, and may be somewhat faster.

As described above, I have figured out how to easily avoid extra cpp flags.
As you note, my approach is simple and fast.  I rest my case.

ok@quintus.uucp (Richard A. O'Keefe) (11/24/88)

Instead of tinkering with the definition of an existing construct,
why not do the sanitary thing and add a new one?
	#use {filename}
resolve a "filename" or <filename> as #include would, and if that
resolved file name has already been #included or #used, do nothing
otherwise #include it.  Easy to do, can't break old code, doesn't
require changes to the file being used.  In fact, if you record
the usage of the filename when the #include or #use is _started_,
you can even have files which #use each other (this can be useful).

greg@cantuar.UUCP (G. Ewing) (11/24/88)

Instead of including the same header file several times, what's
wrong with having the header file define a macro, including it
once, and then using the macro several times?

Using Larry Campbell (campbell@redsox.UUCP)'s example (sort of):

------------------------------message.h------------------------------
#define GENERATE_MESSAGES \
MSG(err_you_lose, "you lose big time, pal") \
MSG(err_get_real, "get real, pal") \
MSG(err_swap_mad, "swap read error, you lose your mind") \
#undef MSG
---------------------------end of message.h--------------------------

------------------------------message.c------------------------------
#include "message.h"

#define MSG(name, string) char name[] = string;
GENERATE_MESSAGES

#define MSG(name, string) extern char name[];
GENERATE_MESSAGES
---------------------------end of message.c--------------------------

Hmm... that perhaps wasn't the most useful of examples, but you get
what I mean, I hope. The point is that cpp could safely be hacked
to only ever include things once; anything that can be done with
multiply included files can also be done using macros.

Can anyone think of something that couldn't?

(P.S. I'd allow a backward-compatibility flag, before anyone jumps
on me...)

Greg Ewing				Internet: greg@cantuar.uucp
Spearnet: greg@nz.ac.cantuar		Telecom: +64 3 667 001 x8357
UUCP:	  ...!{watmath,munnari,mcvax,vuwcomp}!cantuar!greg
Post:	  Computer Science Dept, Univ. of Canterbury, Christchurch, New Zealand
Disclaimer: The presence of this disclaimer in no way implies any disclaimer.

henry@utzoo.uucp (Henry Spencer) (11/26/88)

In article <738@quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:
>Instead of tinkering with the definition of an existing construct,
>why not do the sanitary thing and add a new one?
>	#use {filename}
>... Easy to do, can't break old code, doesn't
>require changes to the file being used.

Well, leaving the existing construct alone (except perhaps in the presence
of a compiler flag) is the right thing to do, but "doesn't require changes
to the file being used" is a bug, not a feature.  In most cases, the fact
that #including a file n times is the same as including it once is a
property of the file being included, not the file doing the including.
The probability of trouble and mistakes will be much lower if it's the
file being included that determines whether future inclusions have any
effect.  The Waterloo "#pragma idempotent" strikes me as the right method;
among other things, it means that your code is portable.
-- 
Sendmail is a bug,             |     Henry Spencer at U of Toronto Zoology
not a feature.                 | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

ok@quintus.uucp (Richard A. O'Keefe) (11/26/88)

In article <867@cantuar.UUCP> greg@cantuar.UUCP (G. Ewing) writes:
>Instead of including the same header file several times, what's
>wrong with having the header file define a macro, including it
>once, and then using the macro several times?

It requires that the macro processor be able to handle very large
macros, which might not otherwise be required.

Also, it is not possible for the two approaches to be equivalent
(consider the built-in macros __FILE__ and __LINE__).

>The point is that cpp could safely be hacked
>to only ever include things once; anything that can be done with
>multiply included files can also be done using macros.

But not using the existing sources.

If you don't like #include, use something else.  It is bad manners to
break other people's tools, without even asking them.

As an example of a company with good manners, the High C compiler has
a pragma which goes something like this:
	pragma   Include(<file>);	/* or   Include("file"); */
	pragma C_Include(<file>);	/* or C_Include("file"); */
where C_Include includes the file only if it hasn't already been included.
(This is "pragma", by the way, not "#pragma".)

Note that at least in UNIX systems, it is possible to
	#include "/dev/tty"
{This can actually be useful, if you know what you're doing.}
There are other kinds of files where the contents may be different,
_by design_, when you open them again.

ok@quintus.uucp (Richard A. O'Keefe) (11/27/88)

In article <1988Nov25.180309.9323@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>In article <738@quintus.UUCP> ok@quintus.UUCP (Richard A. O'Keefe) writes:
>>	#use {filename}
>>... Easy to do, can't break old code, doesn't
>>require changes to the file being used.
>
>Well, leaving the existing construct alone (except perhaps in the presence
>of a compiler flag) is the right thing to do, but "doesn't require changes
>to the file being used" is a bug, not a feature.  In most cases, the fact
>that #including a file n times is the same as including it once is a
>property of the file being included, not the file doing the including.

Which is why "doesn't require changes to the file being used" is NOT
a design error.  If it is the case that including the file N times is
ok, then the file doesn't NEED _any_ changes.  If it is not the case
that including the file N times is ok, then a construct like this should
not be used.  What I had in mind is the situation where someone *else*
owns the header file in question, so that you _can't_ change it.  Ok,
you can get around that by interposing your own file which says
#pragma include_once; #include "the-real-file"
but you can just as easily do that by putting
	#ifndef	FOOBAZ_H
	#include "real_foobaz.h"
	#define	FOOBAZ_H 1
	#endif
in your "foobaz.h" file.  Actually, now that I come to think of it,
just how hard _is_ it to write a 4-line wrapper in order to make anmult
idempotent version of a header?

I'm afraid that it is not really the case that idempotence is "a property
of the file being included, not the file doing the including.", only if
the file being included contains no identifiers which could be #defined
by the caller.  Suppose we have
	extern int strlen(char*);
in a file strlen.h.  Now consider
	#define int double
	#define strlen atof
	#include "strlen.h"
	#undef int
	#undef strlen
	#include "strlen.h"
Not a terribly reasonable thing to do, but sufficient to show that the
idempotence of even such an innocent header as that IS dependent on the
includer, so that it *is* advisable for the includer to explicitly
promise not to break it.  "In most cases", true, this doesn't happen,
but that is a property of the includer!

rfg@nsc.nsc.com (Ron Guilmette) (11/27/88)

In article <1988Nov25.180309.9323@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>In most cases, the fact
>that #including a file n times is the same as including it once is a
>property of the file being included, not the file doing the including.

Absolutely right Henry.  I realized this very fact just before I suggested
using the sid-uid bit on header files which are not to be included more
than once.

In hindsight though, I now realize that even that was a silly suggestion.
I now think that putting something like:

	#once

or

	#pragma once

into such header files would be more appropriate, more clear, and a more
portable solution.

>The probability of trouble and mistakes will be much lower if it's the
>file being included that determines whether future inclusions have any
>effect.

Exactly right.

>The Waterloo "#pragma idempotent" strikes me as the right method;
>among other things, it means that your code is portable.

Is this (semantically) the same thing as I am suggesting?  I have never
heard of this before!  If it has the effect of forcing the file it is
found within to only be included once then I guess that there is at least
one clear (although not yet standard) precedent for an approach to this
problem.

campbell@redsox.UUCP (Larry Campbell) (11/28/88)

In article <8080@nsc.nsc.com> rfg@nsc.nsc.com.UUCP (Ron Guilmette) writes:
}In article <1988Nov25.180309.9323@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
}In hindsight though, I now realize that even that was a silly suggestion.
}I now think that putting something like:
}
}	#once
}or
}	#pragma once
}
}into such header files would be more appropriate, more clear, and a more
}portable solution.
 ...
}>The Waterloo "#pragma idempotent" strikes me as the right method;
}>among other things, it means that your code is portable.

How portable can something be that's not even implemented yet???  If you
just wrap your header files like this:

	#ifdef foo_included
	 ...
	#define foo_included
	#endif

it achieves the result you desire, completely portably, today, in both
C++ and C, without requiring changes to preprocessors or funky #pragma
statements (Dr. Coggins' esthetic distaste for this much pragmatism
notwithstanding).

Really, I think this is a SOLVED PROBLEM that's been getting beaten to
death.  Could we move on to more interesting aspects of the import/export
question than how to get it done with the current syntax-oriented paradigm?
-- 
Larry Campbell                          The Boston Software Works, Inc.
campbell@bsw.com                        120 Fulton Street
wjh12!redsox!campbell                   Boston, MA 02146

akwright@watdragon.waterloo.edu (Andrew K. Wright) (11/29/88)

In article <8080@nsc.nsc.com> rfg@nsc.nsc.com.UUCP (Ron Guilmette) writes:
>In article <1988Nov25.180309.9323@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>>In most cases, the fact
>>that #including a file n times is the same as including it once is a
>>property of the file being included, not the file doing the including.
>>
>>The probability of trouble and mistakes will be much lower if it's the
>>file being included that determines whether future inclusions have any
>>effect.
>
>Exactly right.
>
>>The Waterloo "#pragma idempotent" strikes me as the right method;
>>among other things, it means that your code is portable.
>
>Is this (semantically) the same thing as I am suggesting?  I have never
>heard of this before!  If it has the effect of forcing the file it is
>found within to only be included once then I guess that there is at least
>one clear (although not yet standard) precedent for an approach to this
>problem.

I will expand a bit on my original posting about
	#pragma idempotent
(or #pragma once  as Ron Guilmette suggests).

When a file marked with #pragma idempotent is included the first time,
the preprocessor makes a note of this.  If the file is later included
again, the include directive is simply ignored.  Determining when the
file is included "again" means determining when two include paths are
equal.  You might want to go by physical file equality (ie. compare
devices and i-nodes), pathname equality, or string equality.  The
Waterloo compiler uses simple string equality:  two include paths are
considered equal if strcmp() on the argument strings of the include
directives, modulo their quotes or <>s, reports they are equal.  We
think it is a bad idea to build operating system dependant features
such as links or i-nodes into the semantics of the language; C runs
on many non-UNIX systems without such features.  Besides, you will
never get the ANSI committee to agree to such.

Another poster noted that idempotency is not entirely an attribute of
the included file.  This is true; both the includer and the includee
must agree that the file is to have idempotent semantics, or someone
will get surprised.  Thus #pragma idempotent is more than just a statement
that redundant #includes of this file will be ignored; it asserts
that the user is NOT ALLOWED to redefine any names appearing in
such a file.  This matches the ANSI committee's intent for the standard
include files:  the user is not permitted to redefine anything in
<stdio.h> for instance.  (He is permitted to #undef macros to get
real functions).  <stdio.h> can therefore be marked with
#pragma idempotent, as can all the other standard include files.

In fact, It just occurs to me that CPP could mark identifiers brought
in by idempotent include files as non-redefinable.  This has the
obvious advantage that the ANSI rule
"<the several hundred> standard identifiers cannot be redefined"
becomes a compiler enforced rule, not just a vapor rule.
(Personally I hate the idea of several hundred reserved words, but
 the committee seems to be well on its way down that path.)

Lastly, #pragma idempotent  is upwards compatible.  It does not
break *any* (not just few) existing C programs.  It does have
the disadvantage that pragmas are not required (by ANSI) to be
supported by all compilers, so you may still have to wrap #ifndefs
around your include file bodies if you expect to have your code
ported to any C compiler.

Andrew K. Wright      akwright@watmath.waterloo.edu
CS Dept., University of Waterloo, Ont., Canada.

henry@utzoo.uucp (Henry Spencer) (11/30/88)

In article <562@redsox.UUCP> campbell@redsox.UUCP (Larry Campbell) writes:
>How portable can something be that's not even implemented yet???  If you
>just wrap your header files [in #ifndef]...
>it achieves the result you desire, completely portably, today, in both
>C++ and C, without requiring changes to preprocessors or funky #pragma
>statements... Really, I think this is a SOLVED PROBLEM ...

Not so.  The problem is that a header wrapped in #ifndef still needs to
be opened, read, and scanned completely every time, which is costly when
the #include relationships are complex and common header files get picked
up many, many times.  One can imagine a tricky compiler which notices
the wrapping and optimizes this case, but that's not going to be easy.

In practice, a header file using "#pragma idempotent" or whatever would
still include the #ifndef wrapper, for the sake of portability.  (There
is no requirement that #pragma be portable, but there *is* a requirement
that ANSI C implementations ignore unrecognized #pragmas.)
-- 
SunOSish, adj:  requiring      |     Henry Spencer at U of Toronto Zoology
32-bit bug numbers.            | uunet!attcan!utzoo!henry henry@zoo.toronto.edu

darin@Apple.COM (Darin Adler) (12/07/88)

In article <1988Nov29.203751.26424@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
> In article <562@redsox.UUCP> campbell@redsox.UUCP (Larry Campbell) writes:
> >How portable can something be that's not even implemented yet???  If you
> >just wrap your header files [in #ifndef]...
> >it achieves the result you desire, completely portably, today, in both
> >C++ and C, without requiring changes to preprocessors or funky #pragma
> >statements... Really, I think this is a SOLVED PROBLEM ...
> 
> Not so.  The problem is that a header wrapped in #ifndef still needs to
> be opened, read, and scanned completely every time, which is costly when
> the #include relationships are complex and common header files get picked
> up many, many times.  One can imagine a tricky compiler which notices
> the wrapping and optimizes this case, but that's not going to be easy.

I have to disagree here, Henry. Writing a compiler (or preprocessor) that
notices the wrapping and optimizes it is extremely easy. While the preprocessor
is scanning a file it simply sets a "start of file" flag which is cleared
when anything besides a comment is seen. If an #ifndef occurs with this flag
set, the preprocessor symbol can be recorded a table, along with the file name.
(Of course, another check to be sure that the #endif is the last non-comment
line in the file as well would be necessary, but that is left as a simple
exercise for the implementor.) Then, any time the same file is included,
that symbol can be checked...if it is defined, the file need not be opened.
Note that even if the system can't recognize "this.h" and <this.h> and
"/this.h" as the same file, there is no problem, since the file protects itself
from multiple inclusion.

> In practice, a header file using "#pragma idempotent" or whatever would
> still include the #ifndef wrapper, for the sake of portability.  (There
> is no requirement that #pragma be portable, but there *is* a requirement
> that ANSI C implementations ignore unrecognized #pragmas.)

Note that the above scheme is superior because it gets the same results as
the pragma without typing it, since you still plan to include the #ifndef
wrapper. I'm ready to recommend to our compiler folks that they implement
the scheme now!
--
Darin Adler					              AppleLink: Adler4
UUCP: {amdcad,decwrl,hoptoad,nsc,sun}!apple!darin     Internet: darin@Apple.com

henry@utzoo.uucp (Henry Spencer) (12/09/88)

In article <21780@apple.Apple.COM> darin@Apple.COM (Darin Adler) writes:
>... Writing a compiler (or preprocessor) that
>notices the wrapping and optimizes it is extremely easy...

How extremely easy or significantly difficult it is depends on how your
compiler is implemented.  However, I concede that I was overly pessimistic
about this, and it's probably the right approach.
-- 
SunOSish, adj:  requiring      |     Henry Spencer at U of Toronto Zoology
32-bit bug numbers.            | uunet!attcan!utzoo!henry henry@zoo.toronto.edu