[comp.lang.c] Making re-#includes harmless--a simple solution?

newman@athena.mit.edu (Ron Newman) (12/10/87)

Many header files begin with something like

  #ifndef  __SOME_UNUSUAL_NAME__
  #define  __SOME_UNUSUAL_NAME__

and end with

  #endif

so that if you #include the file more than once, nothing bad happens.

Why not change the semantics of "#include" to be:  "if, while
processing the current .c source file, I have already included this
file once, then don't include it again"?

That seems a lot cleaner than having to (remember to) put the #ifndef
stuff into every header file.  It would also eliminate problems with
vendors who forget to do this to their standard header files.
Is it too late to fix this for ANSI C??

/Ron Newman
 MIT Project Athena

tim@doug.UUCP (Tim J Ihde) (12/10/87)

In article <2000@bloom-beacon.MIT.EDU>, newman@athena.mit.edu (Ron Newman) writes:
> Why not change the semantics of "#include" to be:  "if, while
> processing the current .c source file, I have already included this
> file once, then don't include it again"?

I don't know if this is in the ANSI definition of include or not, but this
is exactly how the latest version of cpp on System V works.  It simply
remembers each include file that it has read and will ignore it if the same
file shows up again.  This is definatly the way to go.

		tim
-- 
Tim J. Ihde					ihnp4!ctsmain!doug!tim
(201) 535-9897

Ok, we can all agree that this is my fault.

rlk@think.COM (Robert Krawitz) (12/10/87)

In article <2000@bloom-beacon.MIT.EDU> newman@athena.mit.edu (Ron Newman) writes:
]Many header files begin with something like
]
]  #ifndef  __SOME_UNUSUAL_NAME__
]  #define  __SOME_UNUSUAL_NAME__
]
]and end with
]
]  #endif
]
]so that if you #include the file more than once, nothing bad happens.
]
]Why not change the semantics of "#include" to be:  "if, while
]processing the current .c source file, I have already included this
]file once, then don't include it again"?

1)  The same file may have multiple names (symlinks and/or hard
links).  How do you KNOW whether a file has been included?  The only
way is by defining an attribute that only that file will have.  The
easiest way to do this (aside from checking device/inumbers, which is
not portable and may not work in some bizarre cases, or other system
dependent hacks) is to #define a unique name.

2)  There may be equivalent files (e. g. I may have my own version of
something-unusual.h that I want instead of the system version) and I
want to make sure the system version doesn't overload mine.

3)  Sometimes it may be DESIRED to include the same file multiple times.

]That seems a lot cleaner than having to (remember to) put the #ifndef
]stuff into every header file.  It would also eliminate problems with
]vendors who forget to do this to their standard header files.
]Is it too late to fix this for ANSI C??

Vendors who don't do this should fix their files, just like any other
broken code.  Users who don't remember to do this will get screwed,
then they will learn, just like they remember not to dereference null
pointers the first time they don't use a VAX.  Artificial constraints
like this to try to recover from simple programming errors just make
compilers more complicated and bug-prone, and prevent the programmer
from deliberately doing something which may be unobvious but useful.

harvard >>>>>>  |
bloom-beacon >  |think!rlk	Robert Krawitz <rlk@think.com>
ihnp4 >>>>>>>>  .

jss@hector.UUCP (Jerry Schwarz) (12/11/87)

In article <2000@bloom-beacon.MIT.EDU> newman@athena.mit.edu (Ron Newman) writes:
>
>Why not change the semantics of "#include" to be:  "if, while
>processing the current .c source file, I have already included this
>file once, then don't include it again"?
>

We have such a cpp here and it has certain advantages.  But the
problem with putting such a rule into the standard is you then have
to define "same file".  Can <foo.h> be same as "foo.h". Suppose that
because of -I options <foo.h> and <sys/foo.h> resolve to the same
file.  Even worse, suppose "d/foo.h" contains a #include of "g.h" is
this the same as "d/g.h".   You can punt and say that it is up to the
implementor (as the committee has done for where to find "foo.h").
But then have you gained much by adding the rule to the standard.

>Is it too late to fix this for ANSI C??

Almost certainly.  I get a sense that many readers of this group do
not understand how far along the ANSI standard is.   The committee
has been working three or four years.  The draft standard is about to
go out for its second public review.  It now takes a two thirds vote
of the committee to make a change. (Some proposals have won
majorities recently but failed because of the 2/3 rule.)  Any
significant change will require another public review process.
Proposals such as the above or "0b", whatever their merits, are just
too late.  If you want a change you probably have to show not
just that your change is a good idea, but that there is a critical flaw
in the way the current standard addresses whatever area you are
concerned with.  Uglyness, while a flaw, will not count as a critical
flaw in this context.  A contradiction in the proposed standard might.

Jerry Schwarz
Bell Labs
Not a member of X3J11

riddle@woton.UUCP (Prentiss Riddle ) (12/11/87)

newman@athena.mit.edu (Ron Newman) writes:
> Why not change the semantics of "#include" to be:  "if, while
> processing the current .c source file, I have already included this
> file once, then don't include it again"?

But sometimes you *want* to #include a file multiple times.  One
example that comes to mind is the problem of generating essentially the
same code for multiple types of objects.  Redefine a few identifiers or
macros before each #inclusion and you've got it. 

One example of a program that does this trick is c_config, posted to
the net some time back by Steve Pemberton (steve@cwi.nl). 

--- Prentiss Riddle ("Aprendiz de todo, maestro de nada.")
--- Opinions expressed are not necessarily those of Shriners Burns Institute.
--- riddle@woton.UUCP  {ihnp4,harvard}!ut-sally!im4u!woton!riddle

robert@pvab.UUCP (Robert Claeson) (12/11/87)

In article <13395@think.UUCP> rlk@THINK.COM writes:

>1)  The same file may have multiple names (symlinks and/or hard
>links).  How do you KNOW whether a file has been included?  The only
>way is by defining an attribute that only that file will have.  The
>easiest way to do this (aside from checking device/inumbers, which is
>not portable and may not work in some bizarre cases, or other system
>dependent hacks) is to #define a unique name.

How can you be sure that the name you choose is unique, especially if
you use links or symlinks?

-- 
Robert Claeson, System Administrator, PVAB, Box 4040, S-171 04 Solna, Sweden
eunet: robert@pvab
uucp:  sun!enea!pvab!robert

ericb@athertn.Atherton.COM (Eric Black) (12/12/87)

In article <13395@think.UUCP> rlk@THINK.COM (Robert Krawitz) writes:
>In article <2000@bloom-beacon.MIT.EDU> newman@athena.mit.edu (Ron Newman) writes:
>]Why not change the semantics of "#include" to be:  "if, while
>]processing the current .c source file, I have already included this
>]file once, then don't include it again"?

	Because it would be a bad idea.  Robert gives a few good reasons:

>1)  The same file may have multiple names (symlinks and/or hard links).

	And NFS-style mounts that give other aliasing possibilities (we
	do that a lot here).  And, as Jerry Schwarz (and others) pointed out, 
	the various combinations of <file>, "file", and either form with
	directory names prepended along with various -Idir cpp options
	makes it pretty hard to tell when it's the "same file".  It can
	be even harder for nested #includes (no religious wars, please!).

>2)  There may be equivalent files

	We have that case here.  When CPP or the compiler barf because
	of a redefinition caused by including two variants of a header
	file when only one should have been, the error messages given
	to the programmer are sometimes less than clear.  In such cases,
	we can have the #ifdef A_VARIANT_HAS_ALREADY_BEEN_INCLUDED clause
	provoke a real syntax error, such as:
		#ifdef SS_SUBSYSTEM_VARIANT_DEFINED

		!?!?!?!? More meaningful message, which the programmer
			 can find when he edits this "standard" foo.h
			 file to see why it got a syntax error
		#else
		#define SS_SUBSYSTEM_VARIANT_DEFINED

		/* continue with the definitions

		#endif SS_SUBSYSTEM_VARIANT_DEFINED

	Of course, we can nest the two techniques, to allow harmless
	re-inclusion of the one file while still catching the erroneous
	inclusion of a second mutually-exclusive variant.

>3)  Sometimes it may be DESIRED to include the same file multiple times.

	Yes, Virginia, you may want to have something like "recursive"
	header files, where on subsequent inclusions some symbols
	are re-defined, or un-defined.  This could certainly be done
	with separate files, but then you have two files to maintain,
	and if you are changing symbol definitions it's a lot easier
	to coordinate and keep them consistent if they are in one file
	with lots of comments, rather than two.

>Artificial constraints
>like this to try to recover from simple programming errors just make
>compilers more complicated and bug-prone, and prevent the programmer
>from deliberately doing something which may be unobvious but useful.

	We all know that one technique for helping the compiler to
	warn the programmer about a "=" where he really meant "=="
	(like in "if (a = 1)") is to get into the habit of coding
	it as "if (1 == a)"; then if you blow it the compiler can
	complain.  That doesn't mean that we want the compiler to
	silently disallow my coding it the first way!

-- 
Eric Black	"Garbage in, Gospel out"
   UUCP:	{sun!sunncal,hpda}!athertn!ericb
   Domainist:	ericb@Atherton.COM

daveb@laidbak.UUCP (Dave Burton) (12/13/87)

In article <1011@woton.UUCP> riddle@woton.UUCP (Prentiss Riddle ) writes:
>newman@athena.mit.edu (Ron Newman) writes:
>> Why not change the semantics of "#include" to be:  "if, while
>> processing the current .c source file, I have already included this
>> file once, then don't include it again"?
>
>But sometimes you *want* to #include a file multiple times. ...


... I missed the first part of this exchange, so pardon me if
I'm out-of-context.

Why change the semantics of #include anyway?
Using the following technique, you can make #include
behave in both ways:

#ifndef HEADER_H
#define	HEADER_H	1.0	/* version number, or nothing */

#define X
#define Y
#define Z

#endif	/* HEADER_H */

By enclosing the entire header file within the #ifndef/#endif pair
(or just the critical portion), the header may be included several
times without problem.

-- 
--------------------"Well, it looked good when I wrote it"---------------------
 Verbal: Dave Burton                        Net: ...!ihnp4!laidbak!daveb
 V-MAIL: (312) 505-9100 x325            USSnail: 1901 N. Naper Blvd.
#include <disclaimer.h>                          Naperville, IL  60540

gwyn@brl-smoke.ARPA (Doug Gwyn ) (12/13/87)

In article <2000@bloom-beacon.MIT.EDU> newman@athena.mit.edu (Ron Newman) writes:
>Why not change the semantics of "#include" to be:  "if, while
>processing the current .c source file, I have already included this
>file once, then don't include it again"?

Because that's not the way C has worked, and some of us use this capability.
It was proposed at an X3J11 meeting and trounced.

gwyn@brl-smoke.ARPA (Doug Gwyn ) (12/13/87)

In article <385@doug.UUCP> tim@doug.UUCP (Tim J Ihde) writes:
-I don't know if this is in the ANSI definition of include or not, but this
-is exactly how the latest version of cpp on System V works.  It simply
-remembers each include file that it has read and will ignore it if the same
-file shows up again.  This is definatly the way to go.

It definitely is NOT the way to go, and they're going to have to fix it
before they can be ANSI conformant.

tim@doug.UUCP (Tim J Ihde) (12/14/87)

In article <6809@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes:
> In article <385@doug.UUCP> tim@doug.UUCP (Tim J Ihde) writes:
> -It simply
> -remembers each include file that it has read and will ignore it if the same
> -file shows up again.  This is definatly the way to go.
> 
> It definitely is NOT the way to go, and they're going to have to fix it
> before they can be ANSI conformant.

After reading some of the other responses to the initial article, I tend to
agree with you; although this facility is quite helpful in cases where you
are dealing with standard include files that do not have a line like
	#define	CTYPEH
or whatever.

Anyway, in the interum I obtained a copy of the manual page for this version
of cpp.  It lists a #pragma statement as follows:

	#pragma multiple
		Allows the current file to be included more than
		once.  The default action nomultiple causes each
		#include file to be included only once.

So in the true C style, it will allow people who know what they are doing
(or think they do) to go ahead and do it.  I assume that the reason they did
not make the standard functionality (ie multiple includes) the default was
because this would not have helped in cases such as with <ctype.h>.  I think
I would have preferred for them to fix these include files instead of breaking
with the standard.

I also noticed a -I- option to the preprocessor, indicating that -I<dir>
listed directories appearing BEFORE the -I- in the command line are searched
only for "" included files, whereas and -I<dir> directories listed AFTER the
-I- are searched for either "" or <> included files.  Nmake generates command
lines using this format.  This might get around the problem of include
files with the same names if used carefully, but again this is non-standard.

		tim
-- 
Tim J. Ihde					ihnp4!ctsmain!doug!tim
(201) 535-9897

Ok, we can all agree that this is my fault.

gwyn@brl-smoke.ARPA (Doug Gwyn ) (12/15/87)

In article <339@pvab.UUCP> robert@pvab.UUCP (Robert Claeson) writes:
-In article <13395@think.UUCP> rlk@THINK.COM writes:
->1)  The same file may have multiple names (symlinks and/or hard
->links).  How do you KNOW whether a file has been included?  The only
->way is by defining an attribute that only that file will have.  The
->easiest way to do this (aside from checking device/inumbers, which is
->not portable and may not work in some bizarre cases, or other system
->dependent hacks) is to #define a unique name.
-
-How can you be sure that the name you choose is unique, especially if
-you use links or symlinks?

That was rlk's whole point; only the file contents (in particular
the unique #define) can be IMMUNE to links and symlinks.

#ifndef UNIQUE_SYMBOL
#define UNIQUE_SYMBOL
/* useful definitions & declarations here */
#endif

hansen@pegasus.UUCP (Tony L. Hansen) (12/21/87)

< From: tim@doug.UUCP (Tim J Ihde)
< 
< In article <2000@bloom-beacon.MIT.EDU>, newman@athena.mit.edu (Ron Newman) writes:
< > Why not change the semantics of "#include" to be:  "if, while
< > processing the current .c source file, I have already included this
< > file once, then don't include it again"?
< 
< I don't know if this is in the ANSI definition of include or not, but this
< is exactly how the latest version of cpp on System V works.  It simply
< remembers each include file that it has read and will ignore it if the same
< file shows up again.  This is definatly the way to go.

(I hadn't seen anything else come back yet on the net refuting this.)

The latest distributed System V version of CPP does NOT do this automatic
checking for multiple #includes of a file. The author of the above article
had installed nmake on his machine, which comes with its own version of CPP,
but hadn't realized that CPP had changed.

					Tony Hansen
				ihnp4!pegasus!hansen, attmail!tony

ark@alice.UUCP (12/22/87)

The trouble with changing the meaning of #include is that
it is sure to break some programs.  Instead, how about this:

	#include {x}
	#include 'x'

Saying {x} is like saying <x> except that you only want x once.
Saying 'x' is like saying "x" except that you only want x once.