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