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!henryjpl@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!jplmatt@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