[net.unix-wizards] Let ME try for a new topic...

danny@itm.UUCP (Danny) (05/30/84)

[ereh no gniog synnuf gnihtemos] (hold your terminal up to a mirror)

I recently discovered the first (to me, anyway) truly awkward
idiosyncrasy of C.  I'm hoping to be enlightened by some
wizards (and, thereby, earn more brownie points toward becoming a
wizard myself) so that, in the future, I will have a clever out.

    Now, on my (a Version 7) C compiler, there may be many
references to an external variable, but only one definition.
The logical place, when dealing with many source files, is to
put the definitions into the "main" (or, more succinctly, the
file that contains the function "main"), and put the references
into a .h file and whichever sources need the refs may include it.

    Ok, so far.  I've seen many programs that do this very thing.
But, this also creates the problem of having to update *two* files
every time an external is added, modified, or deleted.  And, being
human (or in Spaf's case Vegan) it's easy to forget to update both.

    I have seen the hack of preceding each reference with "extern", then
in "main" having a sequence of:

            # define extern
            # include "foo.h"
            # undef extern

    Well, it works, there is only one copy of each, everyone is happy,
except me.  It looks like a hack, it feels like a hack.  Is it possible
that it *is* a hack?

    So, great wizards, humble yourselves, cast your eyes upon me, and
have great compassion.  Remove from my eyes the smoke that blinds,
from my mind the spirits that slow, and from my back the frog that
causes warts.

    Ok, so what's *your* favorite way to deal with this perennial
problem?

                                    Sincerely, I thank you in advance,

                                    Danny
-- 
				Daniel S. Cox
				(akgua!itm!danny)

gwyn@brl-vgr.ARPA (Doug Gwyn ) (06/01/84)

The #define extern /**/ trick only works if you want your extern
storage definitions to be filled with zeros.  I think it is better
to init the extern data explicitly (even if it is zero) in a module
that deals with the data.  Better still, reduce the global data to
an insignificant amount (pass arguments to functions as parameters
and collect data structure manipulators into a single module with
the data made file-static).

henry@utzoo.UUCP (Henry Spencer) (06/03/84)

Danny Cox asks, in essence, how to avoid having to keep two sets of
code "in sync" when you've got external variables defined in one
place but referenced in lots of others.

Sorry, Danny, but I can't resist:  my favorite way of dealing with
the problem is to avoid external variables.  Passing information as
parameters and returned values is a much cleaner way of communicating
between different modules.  I know, this doesn't help much if you
have to contend with existing code that's rife with external variables...
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry

jpl@allegra.UUCP (John P. Linderman) (06/03/84)

externs and include files are two subjects guaranteed to raise my blood
pressure.  The decision by certain compiler purveyors that variables
could be "defined" (i.e. appear without an extern) only once caused
many useful programs to be uncompilable.  Many of us considered that it
was more important to keep software working than to satisfy some
bureaucrat's concept of "clean coding".  (Please spare me chapter 11,
verse 2 from K&R.)  We adopted the following artifice to simplify
maintenance of code that included external variables in .h files.
If the include file used to look like

    int i;
    char foo[] = "bar";

we would replace them with

    #ifdef  OWNER
    #define EXTERN
    #define EXTINIT(X) = X
    #else
    #define EXTERN extern
    #define EXTINIT(X)
    #endif  OWNER

    EXTERN int i;
    EXTERN char foo[] EXTINIT("bar");

In the makefile, we would then include a -DOWNER flag in exactly one
cc line, usually the one for the main routine.  This made it easy to
get programs working again without having to duplicate headers or
rewrite code.

Speaking of include files, I get constant complaints from users whose
code won't compile, either because they included <sys/stat.h> but not
<sys/types.h> (which stat.h requires, at least under 4.2), or because
they managed to include <sys/types.h> twice.  The latter problem is
particularly annoying because it is so easy to fix.  All include
files ought to be of the form

#ifndef typesdefined
#define typesdefined
<former contents of types.h file>
#endif  typesdefined

so multiple inclusions cause no problems.  I am hesitant to fix this
unilaterally, because then programs that work fine here might not
compile elsewhere.  But it ought to be fixed, both under Berkeley
and System 5.  Once that fix is in for all header files, it would be
possible for <sys/stat.h> to do its own include of <sys/types.h>,
since repeated inclusions would do no semantic damage.  This proposal
is microscopically harder to defend: the redundant includes might
make compiles slower.  I find it hard to get excited about slower
compiles, but I'm willing to accept that there may be places where
it is important.

John P. Linderman  Department of War on Morons  allegra!jpl

matt@oddjob.UChicago.UUCP (Matt Crawford) (06/05/84)

A better solution than bracketing the contents of every include file with:
	#ifndef unique_symbol
	#define unique_symbol
	   :
	   :
	#endif unique_symbol

seems to be used for certain 4.2 distribution files.  When one file, say
foo.h, requires definitions in another, say bar.h, it begins with:
	#ifndef bar_unique_symbol
	#include <bar.h>
	#endif bar_unique_symbol
	   :
	   :
This can, of course, be coupled with the above.  It does nothing for
the extern problem, however.
___________________________________________________________
Matt		University	ARPA: crawford@anl-mcs.arpa
Crawford	of Chicago	UUCP: ihnp4!oddjob!matt

rcd@opus.UUCP (Dick Dunn) (06/06/84)

>Sorry, Danny, but I can't resist:  my favorite way of dealing with
>the problem is to avoid external variables.  Passing information as
>parameters and returned values is a much cleaner way of communicating
>between different modules...

Unfortunately, this has nothing to do with the issue.  Parameters and
global variables solve two different problems; it's comparatively seldom
that you have a real choice as to which is appropriate.  Please remember
that parameters have limited scopes and extents (lifetimes).
-- 
Dick Dunn	{hao,ucbvax,allegra}!nbires!rcd		(303)444-5710 x3086
	...Never offend with style when you can offend with substance.

stephenf@elecvax.UUCP (06/07/84)

Re: include files including other files.

	I personally think this is a good idea, but it has slightly
wider implications, at least on our system. We have a makefile which
controls making of the entire source on our system. Individual programs
are given dependancies largely based on include files, and these
dependancy lists are often generated automatically. If someone is
adding a new program to the makefile, they typically grep for "include"
to get the dependancies. If system include files included others, then
they would miss the other include files that were not directly included
in the program.
	I still think it's a good idea - I'm just pointing out one
implication of it. Are there others?


						- Stephen Frede

						University of New South Wales
						Australia

					...decvax!mulga!stephenf:elecvax

henry@utzoo.UUCP (Henry Spencer) (06/10/84)

Dick Dunn comments:

   >Sorry, Danny, but I can't resist:  my favorite way of dealing with
   >the problem is to avoid external variables.  Passing information as
   >parameters and returned values is a much cleaner way of communicating
   >between different modules...
   
   Unfortunately, this has nothing to do with the issue.  Parameters and
   global variables solve two different problems; it's comparatively seldom
   that you have a real choice as to which is appropriate.  Please remember
   that parameters have limited scopes and extents (lifetimes).

He's made the same mistake as several people that sent mail to me
privately:  he has confused the words "external" and "global".  I did
not say "global variables are bad", I said "external variables are bad".
I never said that variables with longer lifetimes than a single function
invocation are a poor idea, because often they are both necessary and
appropriate.  What I said was that variables that are visible all over
the place, i.e. are used for "communicating between different modules",
are bad.  Such communication is much simpler and cleaner if it goes by
a more restricted interface, i.e. function calls.

I use global variables a lot.  But practically all of them have the
magic word "static" in their declarations, so they are visible only
within the source file (the handiest definition of "module" in C) in
which they are defined.  The number of external variables I use, even
in large many-module programs, can be counted on one's thumbs.  I have
experienced no trouble in working this way, and I highly recommend it.
Such programs are a lot easier to understand than almost anything that
needs an "externs.h" file.
-- 
				Henry Spencer @ U of Toronto Zoology
				{allegra,ihnp4,linus,decvax}!utzoo!henry