ark@alice.UUCP (Andrew Koenig) (08/21/90)
In article <12249@netcom.UUCP>, ergo@netcom.UUCP (Isaac Rabinovitch) writes: > On the other hand, you can say (and I used to) that using 1 and > 0 instead of TRUE and FALSE is a similar "familiar practice", since any > competant C programmer knows that C booleans are just integers. > In this case it probably makes a big difference that TRUE and FALSE are > ordinary English words, not obscure acronyms. Unfortunately, using TRUE and FALSE opens a great gaping hole: one would expect that x == TRUE would mean the same as x != FALSE but of course it doesn't -- at least not if you use the usual C convention that 0 is false and any other value is true. > I recently came up against a similar clash of "familiar concepts" in > C. People were arguing (was it in this group?) over why programmers > use "i" instead of "i == 0". Well, for one thing, if (i) foo(); and if (i == 0) foo(); mean precisely the opposite of each other. :-) -- --Andrew Koenig ark@europa.att.com
kdq@demott.COM (Kevin D. Quitt) (08/22/90)
In article <11215@alice.UUCP> ark@alice.UUCP (Andrew Koenig) writes: >In article <12249@netcom.UUCP>, ergo@netcom.UUCP (Isaac Rabinovitch) writes: > >> On the other hand, you can say (and I used to) that using 1 and >> 0 instead of TRUE and FALSE is a similar "familiar practice", since any >> competant C programmer knows that C booleans are just integers. >> In this case it probably makes a big difference that TRUE and FALSE are >> ordinary English words, not obscure acronyms. > >Unfortunately, using TRUE and FALSE opens a great gaping hole: >one would expect that > > x == TRUE > >would mean the same as > > x != FALSE > >but of course it doesn't -- at least not if you use the usual C >convention that 0 is false and any other value is true. We use #define TRUE (1==1) #define FALSE (1!=1) defined that way for hysterical raisins (broken compiler). We are aware of the problem above, and never test booleans that way. TRUE and FALSE are used for assignment purposes only. It makes the intent of the code more obvious. -- _ Kevin D. Quitt demott!kdq kdq@demott.com DeMott Electronics Co. 14707 Keswick St. Van Nuys, CA 91405-1266 VOICE (818) 988-4975 FAX (818) 997-1190 MODEM (818) 997-4496 PEP last 96.37% of all statistics are made up.
david@csource.oz.au (david nugent) (08/28/90)
In <514@demott.COM> kdq@demott.COM (Kevin D. Quitt) writes: > >Unfortunately, using TRUE and FALSE opens a great gaping hole: > >one would expect that > > > > x == TRUE > > > >would mean the same as > > > > x != FALSE > > > >but of course it doesn't -- at least not if you use the usual C > >convention that 0 is false and any other value is true. > We use > #define TRUE (1==1) > #define FALSE (1!=1) > defined that way for hysterical raisins (broken compiler). We are > aware of the problem above, and never test booleans that way. TRUE and > FALSE are used for assignment purposes only. It makes the intent of the > code more obvious. Shouldn't # define FALSE 0 # define TRUE (!FALSE) _always_ work? I have been using this for a considerable amount of time, and so far haven't seen any problems with it. Or are there some circumstances where this might not cover all cases? Regards, david -- Fidonet: 3:632/348 SIGnet: 28:4100/1 Imex: 90:833/387 Data: +61-3-885-7864 Voice: +61-3-826-6711 Internet/ACSnet: david@csource.oz.au Uucp: ..!uunet!munnari!csource!david
dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) (08/28/90)
In <514@demott.COM> kdq@demott.COM (Kevin D. Quitt) writes:
TRUE and FALSE are used for assignment purposes only. It makes
the intent of the code more obvious.
When I find somebody who really, really, really wants to define TRUE
and FALSE, even somebody who uses them for assignment only, I recommend
the following defines instead:
#define ZERO 0
#define ONE 1
These are so much more clear than TRUE and FALSE, and if you use
them in a test, you know exactly what you're testing!
--
Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com>
UUCP: oliveb!cirrusl!dhesi
volpe@underdog.crd.ge.com (Christopher R Volpe) (08/28/90)
In article <2316@cirrusl.UUCP>, dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes: |> |>When I find somebody who really, really, really wants to define TRUE |>and FALSE, even somebody who uses them for assignment only, I recommend |>the following defines instead: |> |> #define ZERO 0 |> #define ONE 1 |> Ugh. Those say nothing. They don't hint to the boolean nature of the variables being assigned to. They're as useless as the comments in the following code: main() { int i; /* declare variable i */ i=0; /* assign zero to i */ while (!EOF) { /* while not end of file */ i=i+1; /* increment i by one */ if (i>100) /* if i is greater than 100 */ i=0; /* then reset i to zero */ } } ================== Chris Volpe G.E. Corporate R&D volpecr@crd.ge.com
flint@gistdev.gist.com (Flint Pellett) (08/29/90)
volpe@underdog.crd.ge.com (Christopher R Volpe) writes: >In article <2316@cirrusl.UUCP>, dhesi%cirrusl@oliveb.ATC.olivetti.com >(Rahul Dhesi) writes: >|> >|>When I find somebody who really, really, really wants to define TRUE >|>and FALSE, even somebody who uses them for assignment only, I recommend >|>the following defines instead: >|> >|> #define ZERO 0 >|> #define ONE 1 >|> >Ugh. Those say nothing. They don't hint to the boolean nature of the >variables being assigned to. I agree. They may also get you into trouble should you ever wish to change the values of your booleans: Off-hand, I don't know why you would want to, but it is possible that at some point in the future you might have to port to CBD C (Charlie's Brain-Dead C) where TRUE is -1, and you'd be rather embarassed to have this line in your code: #define ONE -1 From a true-to-life example of why names like the above are bad: an old program for a SYSV file system, which knew it needed 14 chars for file names at each level, and was putting 2 character extensions (like ".c" elsewhere), did this: #define TWO 2 #define TWELVE 12 char base_filename[TWELVE]; char filename_ext[TWO]; Then, sure enough, someone wanted it to handle file names it hadn't been set up to handle, (which it should have handled in the first place, but that's another story) like ones with more than 1 character extensions, and with null-terminated names, etc., and we had this in the code: #define TWO 15 #define TWELVE 15 A few years from now (if the program had survived- I killed it; It was a mercy killing) if it had migrated to a BSD file system, we might have seen this: #define TWELVE 256 Please: pick names for constants that say what they are for, (like CharsPerFilenameLevel) not what they are, because what they are for is not going to change, the values might. You'll also avoid ending up with someone using the same constant for two different meanings because the name isn't good enough to prevent it. (If a name like ONE got used throughout a program to represent both boolean TRUE and buffers that needed to be 1 char long, and you wanted to lengthen the buffers, you'd have a nice mess to sort out.) -- Flint Pellett, Global Information Systems Technology, Inc. 1800 Woodfield Drive, Savoy, IL 61874 (217) 352-1165 uunet!gistdev!flint or flint@gistdev.gist.com
asrap@warwick.ac.uk (Sean Legassick) (08/29/90)
In article <2316@cirrusl.UUCP> dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes: =In <514@demott.COM> kdq@demott.COM (Kevin D. Quitt) writes: = = TRUE and FALSE are used for assignment purposes only. It makes = the intent of the code more obvious. = =When I find somebody who really, really, really wants to define TRUE =and FALSE, even somebody who uses them for assignment only, I recommend =the following defines instead: = = #define ZERO 0 = #define ONE 1 = =These are so much more clear than TRUE and FALSE, and if you use =them in a test, you know exactly what you're testing! This has to be one of the most useless suggestions I have read on this newsgroup. How about some more defines: #define TWO 2 #define THREE 3 #define FOUR 4 .... #define NINE 9 And how about some more: #define EQUALS == #define LESSTHAN < ...... ad infinitum Okay, so maybe I'm going over the top a little - but you get my point. Personally I go further than simply using TRUE and FALSE (which by the way I always define as (1==1) and (1==0), not for portability but for a little extra clarity and any compiler worth its salt will optimise them down anyway). I have in a standard header file a typedef for bool (which I typedef to an int, although if I used ANSI extensions I could add even more clarity and typedef to an enum { false, true }. This I find is invaluable for clarities sake. An int declaration says to me this variable is going to hold a scalar value - which is very different from a boolean one. I would be interested in anyone who thinks an int declaration of a boolean variable is clearer. The only argument against my method I can think of is that of standardisation - bool isn't a standard C type and shouldn't be as it is still basically an int. However of all the additional types I have seen in people's source, bool is the one I've seen most often. --------------------------------------------------------------------------- Sean Legassick, cuuee@uk.ac.warwick.cu "Improbability factor of one Computing Services asrap@uk.ac.warwick.cu to one. We have normality. University of Warwick Anything you still can't (the walking C obfuscator!) handle is your own problem."
jak@sactoh0.SAC.CA.US (Jay A. Konigsberg) (08/30/90)
In article <2316@cirrusl.UUCP> dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes: >In <514@demott.COM> kdq@demott.COM (Kevin D. Quitt) writes: >> >>TRUE and FALSE are used for assignment purposes only. It makes >>the intent of the code more obvious. > >When I find somebody who really, really, really wants to define TRUE >and FALSE, even somebody who uses them for assignment only, I recommend >the following defines instead: > > #define ZERO 0 > #define ONE 1 > >These are so much more clear than TRUE and FALSE, and if you use >them in a test, you know exactly what you're testing! >-- Pish-posh! TRUE and FALSE are much clearer. Kevin is correct when he says TRUE and FALSE are used for assignment purposes only. The resulting values are then easly checked in standard C syntax making the code more self-documentating. Personally though, I like the following: #define TRUE -1 Why? Because of the following 8 bit pattern: 1: 01111111 -1: 11111111 I mean, after all, if I'm going to set one I might as well set all :-) -- ------------------------------------------------------------- Jay @ SAC-UNIX, Sacramento, Ca. UUCP=...pacbell!sactoh0!jak If something is worth doing, its worth doing correctly.
chip@tct.uucp (Chip Salzenberg) (08/30/90)
According to flint@gistdev.gist.com (Flint Pellett): >[...] it is possible that at some point in the future you might have to >port to CBD C (Charlie's Brain-Dead C) where TRUE is -1 [...] It makes no sense to dispute the portability of the definitions "#define TRUE 1" and "#define FALSE 0". If you can't count on "(1==1)==1" and "(1==0)==0" then you're not using C. The readability and maintainability of code using TRUE and FALSE, however, are still issues. Personally, I like them; but then, I detest the One True Brace Style, so what do I know? :-) -- Chip Salzenberg at Teltronics/TCT <chip@tct.uucp>, <uunet!pdn!tct!chip>
chris@mimsy.umd.edu (Chris Torek) (08/30/90)
>>In <514@demott.COM> kdq@demott.COM (Kevin D. Quitt) writes: >>>TRUE and FALSE are used for assignment purposes only. It makes >>>the intent of the code more obvious. >In article <2316@cirrusl.UUCP> dhesi%cirrusl@oliveb.ATC.olivetti.com (the much-misunderstood Rahul Dhesi) writes: [yes, you have seen it before, but this time read it with a `sarcastic face' in mind] >>When I find somebody who really, really, really wants to define TRUE >>and FALSE, even somebody who uses them for assignment only, I recommend >>the following defines instead: >> >> #define ZERO 0 >> #define ONE 1 >> >>These are so much more clear than TRUE and FALSE, and if you use >>them in a test, you know exactly what you're testing! In article <3835@sactoh0.SAC.CA.US> jak@sactoh0.SAC.CA.US (Jay A. Konigsberg) misses the scarcasm: >Pish-posh! TRUE and FALSE are much clearer. Kevin is correct when >he says TRUE and FALSE are used for assignment purposes only. The >resulting values are then easly checked in standard C syntax making >the code more self-documentating. > >Personally though, I like the following: > >#define TRUE -1 Since this is all going around again (already!), it is probably time for a small addition to the FAQ list: Q: How about Booleans in C? For instance: typedef int bool; #define FALSE 0 #define TRUE 1 A: Some people believe this adds clarity. Beware, however: typedef enum { true, false } bool; /* bug */ bool result; result = a == b; Surprise, this sets `result' to `true' if a!=b, and to `false' if a==b. Or consider #define FALSE 0 #define TRUE -1 if ((a == b) == TRUE) ... This `if' test never runs. Or: if (x) ... vs if (x == TRUE) ... vs if (x != FALSE) ... The middle line does something different from the first and last lines. Q: What about #define FALSE (1==0) #define TRUE (1==1) Since C defines the RESULT of all boolean operators as 0-for-false and 1-for-true (but does not make such a requirement on the TEST value in if, while, etc.), this is needless make-work for the compiler. Some people have suggested that this is useful with broken compilers (where `true' results produce the value -1, for instance), but if the compiler gets something this basic wrong, how can you trust it to produce correct code for anything even moderately complicated? These defines still suffer from the problem that `x==TRUE' means something different from `x!=FALSE'. As a final note, the `best' choice of storage type for a boolean is not necessarily `int' (in some situations a bit array would be best, although C does not provide such directly). If you use an enumerated type, the storage type is up to the compiler; however, the compiler is then free to warn about bool x = a == b; since the result of `a==b' is an `int' and not an enumeration member. (The compiler must accept the expression; it may only generate a warning at most.) -- In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 405 2750) Domain: chris@cs.umd.edu Path: uunet!mimsy!chris
peter@ficc.ferranti.com (Peter da Silva) (08/30/90)
In article <3835@sactoh0.SAC.CA.US> jak@sactoh0.SAC.CA.US (Jay A. Konigsberg) writes: > #define TRUE -1 This is C, not Forth. The truth value returned by comparison operators is defined to be "1". -- Peter da Silva. `-_-' +1 713 274 5180. 'U` peter@ferranti.com
volpe@underdog.crd.ge.com (Christopher R Volpe) (08/31/90)
I wouldn't use TRUE and FALSE in comparisons such as if (x==TRUE) for the reasons Chris Torek outlined, but I think they are useful for setting default values, e.g. "int expert_mode = TRUE;". For tests, if you want the extra clarity, how about the following: #define TRUE(x) (x) #define FALSE(x) (!(x)) and then do things like " if (FALSE((x==y) && (x==z)) || whatever) ..." ================== Chris Volpe G.E. Corporate R&D volpecr@crd.ge.com
bomgard@copper.ucs.indiana.edu (Tim Bomgardner) (08/31/90)
In article <1990Aug29.153917.28110@warwick.ac.uk> asrap@warwick.ac.uk (Sean Legassick) writes: >[...] > And how about some more: > > #define EQUALS == > #define LESSTHAN < ...... ad infinitum You're gonna laugh, but I never got Fortran out of my system (so to speak). After a couple coding errors early in my C career of the form if (x = y) ... and if (x << y) ... I put the following defines in a .h file: #define EQ == #define NE != #define LT < #define NOT ! #define AND && #define OR || etc. Another guy I know went even further, with defines such as #define THEN { #define ELSE } else { #define ENDIF } etc. It actually made converting a lot of old fortran programs to C go pretty quickly. > Okay, so maybe I'm going over the top a little - but you get my point. >Personally I go further than simply using TRUE and FALSE (which by the way >I always define as (1==1) and (1==0), not for portability but for a little >extra clarity and any compiler worth its salt will optimise them down anyway). > I have in a standard header file a typedef for bool (which I typedef >to an int, although if I used ANSI extensions I could add even more clarity >and typedef to an enum { false, true }. This I find is invaluable for >clarities sake. An int declaration says to me this variable is going to hold >a scalar value - which is very different from a boolean one. > I would be interested in anyone who thinks an int declaration of >a boolean variable is clearer. The only argument against my method I can think >of is that of standardisation - bool isn't a standard C type and shouldn't >be as it is still basically an int. However of all the additional types >I have seen in people's source, bool is the one I've seen most often. Well, depending on how you look at it, *everything* is an int (of one length or another). That's no reason to eliminate a data type. Data abstraction is one of the reasons we use high level languages in the first place. typedef bool makes perfect sense and is in keeping with the C tradition of never spelling any word out if it can be avoided (if u cn rd ths, u cn wrt pgms n C). And for all you fortran fans out there, how bout #define LOGICAL int (yes, that's there in my friend's .h file, as well as INTEGER, REAL, etc.). I DEFINE all my case labels, magic numbers, etc., and more often than not my variable names have more than 12 characters instead of less. At least I can look at code I wrote a couple years ago with a good chance that I'll have some clue as to what it was I thought I was doing.
browns@iccgcc.decnet.ab.com (Stan Brown, Oak Road Systems) (08/31/90)
In article <26280@mimsy.umd.edu>, chris@mimsy.umd.edu (Chris Torek) writes: > Since this is all going around again (already!), it is probably time > for a small addition to the FAQ list: > > Q: How about Booleans in C? For instance: > > typedef int bool; > #define FALSE 0 > #define TRUE 1 > > A: Some people believe this adds clarity. Beware, however: > > typedef enum { true, false } bool; /* bug */ > [rest of excellent comments deleted] Chris, I think adding this to the FAQ's is an excellent idea. I like the way you said in your post that it's not settled whether int or enum is better for this. Only suggestion I'd make: you tell how _not_ to write the enum; why not also tell how to write it: typedef enum {false, true} bool; Stan Brown, Oak Road Systems, Cleveland, Ohio, U.S.A. (216) 371-0043 The opinions expressed are mine. Mine alone! Nobody else is responsible for them or even endorses them--except my cat Dexter, and he signed the power of attorney only under my threat to cut off his Cat Chow!
lfd@cbnewsm.att.com (leland.f.derbenwick) (08/31/90)
In article <11369@crdgw1.crd.ge.com>, volpe@underdog.crd.ge.com (Christopher R Volpe) writes: > In article <2316@cirrusl.UUCP>, dhesi%cirrusl@oliveb.ATC.olivetti.com > (Rahul Dhesi) writes: > |> > |>When I find somebody who really, really, really wants to define TRUE > |>and FALSE, even somebody who uses them for assignment only, I recommend > |>the following defines instead: > |> > |> #define ZERO 0 > |> #define ONE 1 > |> > > Ugh. Those say nothing. They don't hint to the boolean nature of the > variables being assigned to. They're as useless as the comments in > the following code: [deleted] Am I the only one out here who thought that "Those say nothing" was _exactly_ the point of the posting? I saw the ZERO/ONE posting as a parody of the TRUE/FALSE suggestion, pointing out gently that #defining TRUE and FALSE is about as useful as ONE and ZERO -- they are hacks for people who don't approve of the original choice made when the language was invented. And defining TRUE and FALSE tends to lead, eventually, to some maintenance programmer who's just (almost) learned C writing if (flag == TRUE) which of course doesn't work at all the way it looks. (Though I'll admit it reads well: "if flag is true...") IMHO, any "improvement" that would lead a novice into this sort of error is a step backwards. Of course, this is now the <n+1>st rehashing of this issue in this newsgroup, and it's essentially become a religious war. -- Speaking strictly for myself, -- Lee Derbenwick, AT&T Bell Laboratories, Warren, NJ -- lfd@cbnewsm.ATT.COM or <wherever>!att!cbnewsm!lfd
karl@haddock.ima.isc.com (Karl Heuer) (08/31/90)
In article <708.26dd1bf9@iccgcc.decnet.ab.com> browns@iccgcc.decnet.ab.com (Stan Brown, Oak Road Systems) writes: >Chris, I think adding this to the FAQ's is an excellent idea. I like the way >you said in your post that it's not settled whether int or enum is better for >this. Only suggestion I'd make: you tell how _not_ to write the enum; why not >also tell how to write it: > > typedef enum {false, true} bool; Item #47 in the FAQ already gives four ways to write it, and mentions some of the tradeoff issues in choosing a type. It could probably be amplified a bit, though. Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint
dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) (08/31/90)
I was brash enough to suggest #define ZERO 0 #define ONE 1 and Sean Legassick chastises me thus: This has to be one of the most useless suggestions I have read on this newsgroup. But in fact my suggested definitions are much more useful than the common recommendation of defining TRUE and FALSE. No matter how careful the original programmer is, sooner or later somebody will come along and, noticing that TRUE means true, will try to do something like: i = isdigit(c); ... if (i == TRUE) ... thereby introducing a bug. If, on the other hand, TRUE were not defined but ONE were available, the coder would probably do: i = isdigit(c); ... if (i == ONE) ... which would immediately make him ask himself: "The return value from isascii() is true, but is it really 1?" Of course, defining ONE and ZERO is fairly silly. But defining TRUE and FALSE is quite risky. I'll take fairly silly over quite risky any day. -- Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com> UUCP: oliveb!cirrusl!dhesi
dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) (08/31/90)
In <1990Aug29.153917.28110@warwick.ac.uk> asrap@warwick.ac.uk (Sean Legassick) explains why he might want to use enum {false, true} for booleans in C and continues: >I would be interested in anyone who thinks an int declaration of >a boolean variable is clearer. It depends. Try this: typedef enum {false, true} bool; ... while ((c = getc(stdin)) != EOF) { bool got_digit; got_digit = isdigit(c); ... if (got_digit == true) { ... } } Since isdigit() is not guaranteed to return 0 or 1, the value of got_digit could be something else (e.g. 4). Then, even when got_digit is true by C conventions (nonzero), it isn't necessarily equal to its enumeration value "true". No matter what you do, you cannot in C get around this: Although the result of a boolean condition is always 0 or 1, all nonzero values are considered to be true in a boolean context. All mechanisms that try to represent this fact using only two values (e.g. TRUE and FALSE, or enum {false, true}) are likely to lead to bad code that looks good. -- Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com> UUCP: oliveb!cirrusl!dhesi
asrap@warwick.ac.uk (Sean Legassick) (08/31/90)
In article <2342@cirrusl.UUCP> dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes:
=In <1990Aug29.153917.28110@warwick.ac.uk> asrap@warwick.ac.uk (Sean
=Legassick) explains why he might want to use enum {false, true} for
=booleans in C and continues:
=
=>I would be interested in anyone who thinks an int declaration of
=>a boolean variable is clearer.
=
=It depends. Try this:
=
[code ommited which demonstrates comparison with true will not work]
=
=Since isdigit() is not guaranteed to return 0 or 1, the value of
=got_digit could be something else (e.g. 4). Then, even when got_digit
=is true by C conventions (nonzero), it isn't necessarily equal to its
=enumeration value "true".
=
=No matter what you do, you cannot in C get around this:
=
= Although the result of a boolean condition is always 0 or 1, all
= nonzero values are considered to be true in a boolean context.
= All mechanisms that try to represent this fact using only two
= values (e.g. TRUE and FALSE, or enum {false, true}) are likely to
= lead to bad code that looks good.
But as has frequently been mentioned in this thread, comparison
to any #defined TRUE and FALSE, and enum { false, true } or even just
0 and 1 cannot be guaranteed to work. Therefore I use a 'bool' type but
I NEVER USE FALSE OR TRUE IN COMPARISONS. In the code you quote I would
say:
bool foo;
foo = isdigit(bar);
if (foo) ...
Guaranteed to work, and blatantly obvious what it does. There's
nothing that says if you use bool types you have to compare to TRUE or FALSE,
you don't do it without them, so why do it with them?
--------------------------------------------------------------------------
Sean Legassick, cuuee@uk.ac.warwick.cu "Improbability factor of one
Computing Services asrap@uk.ac.warwick.cu to one. We have normality.
University of Warwick Anything you still can't
(the walking C obfuscator!) handle is your own problem."
brad@SSD.CSD.HARRIS.COM (Brad Appleton) (08/31/90)
>And defining TRUE and FALSE tends to lead, eventually, to some >maintenance programmer who's just (almost) learned C writing > > if (flag == TRUE) > >which of course doesn't work at all the way it looks. (Though I'll >admit it reads well: "if flag is true...") IMHO, any "improvement" >that would lead a novice into this sort of error is a step backwards. > >Of course, this is now the <n+1>st rehashing of this issue in this >newsgroup, and it's essentially become a religious war. I thought the whole point of having a boolean type was so that one could write: if ( flag1 && !flag2 ) instead of if ( flag1 == TRUE && flag2 == FALSE ). TRUE and FALSE have no place being part of this type of comparison in any programming language that provides a boolean type; they are only needed for assignment. SO why do some people insist on putting them in logical comparisons - its redundant. Maybe instead of #defining or enum'ing TRUE and FALSE we should encourage doing something like: #define SET(flag) flag = 1 /* or -1 or !0 or whatever */ #define CLEAR(flag) flag = 0 and if you want to get fancy: #define TOGGLE(flag) flag = (flag) ? 1 : 0 (or whatever macro names you prefer) and then we would never be tempted to use them in a comparison. It just bothers me when I see (flag == FALSE) instead of (!flag). I guess this is the n+2 rehashing of this tripe! ______________________ "And miles to go before I sleep." ______________________ Brad Appleton brad@travis.ssd.csd.harris.com Harris Computer Systems ...!uunet!hcx1!brad Fort Lauderdale, FL USA ~~~~~~~~~~~~~~~~~~~~ Disclaimer: I said it, not my company! ~~~~~~~~~~~~~~~~~~~
reino@cs.eur.nl (Reino de Boer) (08/31/90)
volpe@underdog.crd.ge.com (Christopher R Volpe) writes: >I wouldn't use TRUE and FALSE in comparisons such as if (x==TRUE) >for the reasons Chris Torek outlined, but I think they are useful >for setting default values, e.g. "int expert_mode = TRUE;". >For tests, if you want the extra clarity, how about the following: >#define TRUE(x) (x) >#define FALSE(x) (!(x)) >and then do things like " if (FALSE((x==y) && (x==z)) || whatever) ..." or, how about: #define FALSE 0 #define TRUE 1 #define BOOL( b ) ( (b) ? TRUE : FALSE ) #define NOT( b ) BOOL( !(b) ) so that NOT( TRUE ) == FALSE holds. -- Reino -- Reino R. A. de Boer "We want to build the right product right, right?" Erasmus University Rotterdam ( Informatica ) e-mail: reino@cs.eur.nl
johnb@srchtec.UUCP (John Baldwin) (09/01/90)
In article <3835@sactoh0.SAC.CA.US> jak@sactoh0.SAC.CA.US (Jay A. Konigsberg) writes: > >Personally though, I like the following: > >#define TRUE -1 > >Why? Because of the following 8 bit pattern: > > 1: 01111111 >-1: 11111111 Aaaiigh! Never... NEVERNEVERNEVERNEVER *ever* assume 1's-complement, 2's-complement, and so on! The exception to this is when writing very machine-specific code which you already *know* isn't going to be portable anyway. Even then, there are some tricks to isolate yourself from the architecture, without giving up much. P.S. what architecture gives 1(decimal) == 01111111 (binary) ??? -- John T. Baldwin | johnb%srchtec.uucp@mathcs.emory.edu Search Technology, Inc. | | "... I had an infinite loop, My opinions; not my employers'. | but it was only for a little while..."
dts@quad.sialis.mn.org (David T. Sandberg) (09/01/90)
In article <664@csource.oz.au> david@csource.oz.au writes: >Shouldn't > > # define FALSE 0 > # define TRUE (!FALSE) > >_always_ work? Sorry, this is no different than defining TRUE as 1... (!FALSE) is (!0) is (1), after all. My two cents: I define FALSE as 0 and TRUE as 1 on a regular basis, but only use them to make assignments to flag variables more self- documenting. I seldom use the defines for the conditional tests of those variables, and _never_ use them to test the zero or nonzero condition of any function or expression which doesn't explicitly use those same defines in yielding it's result (that means no standard library functions or macros, for starters). When I do need to save the result of such things in a variable being used as a boolean, I do so using the longer (but safer) method: int gotdigit = isdigit(c) ? TRUE : FALSE; (This could be made into a BoolValue() macro if one wished to do so. I haven't.) It seems to me that these defines can be quite helpful and safe if the programmer maintains some level of discipline and restraint in deciding where it is appropriate to use them. In fact, at the restricted level in which they appear in my code, TRUE could be defined as any nonzero value without adversely affecting it's functionality. If changing TRUE's value would break any given expression, then that expression is one which in my opinion should have been considered to be out of the scope of these defines in the first place. -- \\ \ David Sandberg, consultant \\ // "Small hats!" / Richfield MN // \\ \ dts@quad.sialis.mn.org \\
flee@guardian.cs.psu.edu (Felix Lee) (09/02/90)
>#define BOOL( b ) ( (b) ? TRUE : FALSE ) >#define NOT( b ) BOOL( !(b) ) Personally, I use this set of macros: #define FALSE 0 #define CNAND(a,b) (!((a)&&(b))) #define CNOT(a) CNAND(a,a) #define CXOR(a,b) CNAND(CNAND(a,CNOT(b)),CNAND(b,CNOT(a))) #define CEQUIV(a,b) CNOT(CXOR(a,b)) #define COR(a,b) CEQUIV(a,CNAND(CNOT(a),CXOR(a,b))) #define CAND(a,b) CXOR(COR(a,b),CXOR(a,b)) #define TRUE COR(FALSE,CNOT(FALSE)) #define ISTRUE(a) CAND(TRUE,a) #define ISFALSE(a) CNOT(ISTRUE(a)) These may unfortunately overrun some compiler or preprocessor limits (TRUE expands to an 853 character expression, and ISFALSE(x) expands to 203677 characters). But they're otherwise quite portable, and I find the prefix style much more readable than C's cryptic infix expressions, especially when used in conjunction with a set of macros that provide LISP-ish control structures. -- Felix Lee flee@cs.psu.edu
flint@gistdev.gist.com (Flint Pellett) (09/04/90)
I've received mail from a couple people flaming me for not reading/knowing that C evaluates booleans to 0/1, when I commented that using the named constants ZERO/ONE for boolean values was a bad idea. They missed my point. I apparently also missed Rahul's point/ sarcasm-- sorry. The fact that C evaluates booleans to 0 or 1 and always will has very little to do with what I said in my posting. They have assumed that a boolean is only useful to be evaluated by C, and that is NOT true. (As has been demonstrated in other notes here, using it for purposes other than assigning vars from it is generally not good.) It is quite possible that a program is written which contains variables used to tell a TRUE/FALSE result, where none of those variables are ever used in a comparison within the program itself. For example, those truth values may only be stored away in some database that is read by a different program. If the different program is C right now, you'll define TRUE=1 and FALSE=0. Next year, you may rewrite the different program in some other language where TRUE is 0, and FALSE is 1, (maybe because some contract requires a certain language for some parts of the system), change the database structure, and you would then have to redefine the constants in your C program. Having a "#define ONE 0" in my code is not something I would want. (This also has nothing to do with the argument about how good/bad it is to have TRUE/FALSE defined because they can be misused by ignorant programmers. __Properly used__, a simple and easy to compile 0/1 set of constants works.) -- Flint Pellett, Global Information Systems Technology, Inc. 1800 Woodfield Drive, Savoy, IL 61874 (217) 352-1165 uunet!gistdev!flint or flint@gistdev.gist.com
martin@mwtech.UUCP (Martin Weitzel) (09/04/90)
In article <585@quad.sialis.mn.org> dts@quad.sialis.mn.org (David T. Sandberg) writes: >In article <664@csource.oz.au> david@csource.oz.au writes: [and many many others wrote about their preference for #defining TRUE and FALSE over simply using good ol' plain 1 and 0] I quote the following only as an example: >My two cents: I define FALSE as 0 and TRUE as 1 on a regular basis, >but only use [... followed by about 10 more lines of rules when and when not to use TRUE and FALSE in statements] People! Aren't you realizing what you are doing here? K&R tried to make this very easy with C ... they LEFT OUT out a "boolean" or "logical" datatype and gave *very simple* rules for using int for that purpose. You need no more than these two sentences to completly describe the behaviour: In every context where the further execution depends on a truth value of some expression, the value 0 is taken to mean FALSE and any other value is taken to mean TRUE. In any context where the execution of a builtin operator yields a truth value, this value is 0 for FALSE and 1 for TRUE. But what are YOU doing: You clutter up this beautiful and easy to follow mapping between int-s and truth values with new #defines or enum-s and now of course you need additional rules, in which contexts your augmentations may be safely used and where they are dangerous. In the last an final result, whoever want's to use your TRUE and FALSE must nevertheless understand the *whole* story, until he or she can do it right all the time. But isn't a claimed goal of some of you to make C easier to write by defining TRUE and FALSE? No, it should only be easier to read? So let me ask you: Why should anybody read your code if this person hasn't sooner or later to modify this code or take at least part of it as a template for own work? And how have you made sure that the readers have grasped *your* strict rules when and when not to use TRUE and FALSE and will apply it correctly? Now, if you think it's such a bad idea to set a variable which has only to reflect two states to the values 0 and 1 why don't you call the formal constants somthing like SET, YES, ON, SUCCESS, ... (and their counterparts CLEAR, NO, OFF, FAILURE, ...)? Is it because most of you (still) grew up with PASCAL? And a final word to the ones who advocate "strong typing": You should clearly vote for differently typed truth values (a la "new ...." in ADA) - which of course are not supported by the C-compiler, but can at least be done for "the eyes of the reader": If you are reluctant when it comes to mixing (integer) numbers with truth values, you should clearly vote for *differently named* constants for different sorts of truth values. Take the following example: enum bool { FALSE, TRUE }; bool fast_tty, can_scroll; ..... /* if TTY is fast, we assume it can scroll */ can_scroll = fast_tty; Isn't this a bit like substituting apples for oranges? Shouldn't we rather write: can_scroll = (fast_tty == TRUE) ? TRUE : FALSE; If you strongly favour the latter for some reason, why not clarify it further: enum { SLOW_TTY, FAST_TTY } tty_speed; enum { NO_SCROLL, CAN_SCROLL } scroll_mode; ... scroll_mode = (tty_speed == FAST_TTY) ? CAN_SCROLL : NO_SCROLL; Here we completly avoid TRUE and FALSE (of course we have to think hard and invent other "good" names for our purpose), but we can later make easly additions, eg. enum { NO_SCROLL, JUMP_SCROLL, SMOTH_SCROLL } scroll_mode; And those of you who are paid based on how much lines of code you write per month should also feel comfortable with this proposual :-) -- Martin Weitzel, email: martin@mwtech.UUCP, voice: 49-(0)6151-6 56 83
dts@quad.sialis.mn.org (David T. Sandberg) (09/04/90)
In article <898@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes: >In article <585@quad.sialis.mn.org> I wrote: >>My two cents: I define FALSE as 0 and TRUE as 1 on a regular basis, >>but only use [... followed by about 10 more lines of rules when and > when not to use TRUE and FALSE in statements] [...which was actually ten lines of explanation for just one rule, partly because I do tend to be wordy, and partly in order to be certain that I was not misunderstood and thereby avoid responses which border on flamage criticizing understated elements of my rationale. Silly me.] >................................. how have you made sure that the >readers have grasped *your* strict rules when and when not to use >TRUE and FALSE and will apply it correctly? Speaking for myself, I realize that this is a weak point in my own TRUE-FALSE philosophy. However, "my strict rules" can be quite adequately summarized by a single comment next to the definition: #define TRUE 1 /* for assignments to flags ONLY! */ #define FALSE 0 /* likewise */ Is this really that difficult to fathom? Sure, the next programmer could ignore it and use TRUE and FALSE in too general of a context anyway, but he could also similarly abuse 90% of anyone's code - for example, using local variables for multiple unrelated tasks in a function. So, how often do you bother to write comments like this? int c; /* for the return value of getch() ONLY! */ Not often, I'd guess. We have to depend on other programmers to have some modicum of sense, after all, don't we? If someone is going to test the results of isalpha() or whatever against a macro without even knowing what the macro is and is for, I am tempted to say that they need to spend the extra ten minutes looking for the problem so as not to repeat it in the future. But saying it would probably bring on more responses bordering on flamage, so I won't. (-: IMVHO, the whole point of having TRUE and FALSE available is to help differentiate setting flags from arithmetic operations, and without having to create a boolean data type. Maybe my problem is that I write structured(TM) code to a fault, commonly using flags to exit loops or do error handling, rather than using goto or break. When the fabled next programmer comes along to read/support my code, I expect something like "done = FALSE;" to be a lot more intuitive than "done = 0;", since the latter could easily be arithmetic in nature, whereas TRUE or FALSE should immediately imply that "done" is a flag. So, either the next programmer can take a moment to learn what TRUE and FALSE actually are, or he can go wading through code everytime he sees 1 or 0 assigned to an int, to see if the result is being used as a flag variable as opposed to an arithmetic one. You can weigh those tradeoffs as you see fit: the defines seem reasonably to me, given my coding style. (This will now undoubtedly degenerate into a style war, in which I will not take part.) >............................................... why don't you call >the formal constants somthing like SET, YES, ON, SUCCESS, ... (and >their counterparts CLEAR, NO, OFF, FAILURE, ...)? Is it because >most of you (still) grew up with PASCAL? Not in my case at least. And I have actually used descriptive definitions like those you suggest on occasion. But typically my flag variables are named in such a way that TRUE and FALSE "read" better than anything else. (Oh good; now maybe I'll be accused of growing up with COBOL.) -- \\ \ David Sandberg, consultant \\ // "Small hats!" / Richfield MN // \\ \ dts@quad.sialis.mn.org \\
mtr@ukc.ac.uk (M.T.Russell) (09/04/90)
The reason for using TRUE and FALSE rather than 1 and 0 is simple: it lets you say what you mean. Someone once said that `GOSUB 9000' was the most dismal statement in the whole of computing. In my opinion `boolvar = 1;' is in the same class. To the people complaining about `if (x == TRUE)': YOU ONLY USE `TRUE' AND `FALSE' FOR ASSIGNMENT AND PARAMETER PASSING. It's a fairly simple rule. I'd be interested to see some experiments with adding a `bool' type to a C compiler (gcc?), with the following semantics: - bool has the same size and representation as int. - the comparison operators yield bool as the result type. - the `&&', `||' and `!' operators demand bool operands and yield a bool result. The ?: operator demands bool as its first operand. The only other operators that take bool operands are `!=', `==' and `&' (address of). - `if', `do/while', `while' and `for' demand a bool expression as the test. Note that `if (ptr)' is still allowed by the implicit rewrite to `if (ptr != 0)'. - assignment and comparison between bool and arithmetic types is illegal without a cast, except for assignment to a bool lvalue of an integral constant expression with the value 0 or 1. This would provide some useful extra type checking, especially now that we have function prototypes. If `typedef int bool;' was allowed as a special case, then code portability wouldn't be affected. One weakness of this scheme is that it forces bool variables to take the same space as an int. I'm not sure whether adding `shortbool' and `charbool' types (with appropriate conversion rules) would be worth the additional complexity. Programmers can always use casts. BTW: these rules would reject `bool gotdigit = isdigit(c)', and rightly. Mark Russell
peter@ficc.ferranti.com (Peter da Silva) (09/05/90)
In article <2341@cirrusl.UUCP> dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes: > i = isdigit(c); > ... > if (i == TRUE) ... I would never do !if (anything == TRUE)! or !if (anything == FALSE)!. Not only is it redundant, but it obscures the meaning of the code. If you have a boolean-valued variable, it's clearer to say !if (anything)! or, for that matter, !if (!anything)!. The only think I use TRUE and FALSE for is: boolean = TRUE; return TRUE; function(..., TRUE, ...); -- Peter da Silva. `-_-' +1 713 274 5180. 'U` peter@ferranti.com
userAKDU@mts.ucs.UAlberta.CA (Al Dunbar) (09/05/90)
In article <5398@harrier.ukc.ac.uk>, mtr@ukc.ac.uk (M.T.Russell) writes: >... >To the people complaining about `if (x == TRUE)': YOU ONLY USE `TRUE' AND >`FALSE' FOR ASSIGNMENT AND PARAMETER PASSING. It's a fairly simple rule. > Hear, hear!!! I would hazard a guess at this point that perhaps the reason that there is so much contention and misunderstanding here is simply that C doesn't have a 'LOGICAL' (or boolean) type. In certain contexts, an integral value is taken by the compiler to mean False if zero and True if non-zero. If you were forced to provide an expression of boolean type, then the compiler would quickly straighten everyone out, and 'if (x == TRUE)' would never appear in a compiled program. The struggle to make it look like it has a boolean type (such as the macros suggested recently, one of which expanded to over 200K bytes!!!) are not doing much to add clarity to the situation. Unfortunately, the solution is that we should all learn the language a little better. -------------------+------------------------------------------- Alastair Dunbar | Edmonton: a great place, but... Edmonton, Alberta | before Gretzky trade: "City of Champions" CANADA | after Gretzky trade: "City of Champignons" -------------------+-------------------------------------------
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (09/05/90)
In article <5398@harrier.ukc.ac.uk> mtr@ukc.ac.uk (M.T.Russell) writes: > To the people complaining about `if (x == TRUE)': YOU ONLY USE `TRUE' AND > `FALSE' FOR ASSIGNMENT AND PARAMETER PASSING. It's a fairly simple rule. It is wise to take advantage of the language's syntax to remind the programmer of this at every turn. If I were really desperate for this type: typedef struct { int truth; } truefalse; #define set_true(b) ((void) ((b)->true = 1)) #define set_false(b) ((void) ((b)->true = 0)) #define is_true(b) ((b).true) Usage: truefalse flagfoo; set_true(&flagfoo); if (is_true(flagfoo)) ... Somehow in real programs I've never had trouble with declaring variables flagthis, flagthat, flagtheotherthing. The ``flag'' alerts the reader. Flags are multi-valued when I need to express different shades of truth. ---Dan
ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (09/05/90)
In article <5398@harrier.ukc.ac.uk>, mtr@ukc.ac.uk (M.T.Russell) writes: > To the people complaining about `if (x == TRUE)': YOU ONLY USE `TRUE' AND > `FALSE' FOR ASSIGNMENT AND PARAMETER PASSING. It's a fairly simple rule. All of _us_ reading this newsgroup may follow that rule, but as an empirical observation a lot of people _don't_. I have seen too many Pascal programs where people wrote x: boolean; ... if x = TRUE then ... (and in Pascal this _works_) to be sanguine about the programmers in question learning better when they switch to C. I have also seen this kind of code in Algol programs, and if I were reading novice Ada programs I'm sure I'd see it there. I _have_ seen it in C code, and it was wrong. Let's not forget the Lisp Lesson: there is often something useful you could return instead of an anonymous "true". For example, if you are looking for a character in a string, instead of returning TRUE (it is there) or FALSE (it isn't) you could return a pointer to if it it's there or NULL if it isn't. If you program with that in mind, you really don't _care_ much about TRUE/FALSE -vs- 1/0, because it's rare that you have so little to say. > - the `&&', `||' and `!' operators demand bool operands and > yield a bool result. The ?: operator demands bool as its > first operand. The only other operators that take bool > operands are `!=', `==' and `&' (address of). Why should your proposed C variant be more restrictive than Pascal? In Pascal FALSE < TRUE is allowed, meaningful, and true. It is also allowed, meaningful, and true in Ada (LRM 3.5.3). -- You can lie with statistics ... but not to a statistician.
mtr@ukc.ac.uk (M.T.Russell) (09/05/90)
In article <3686@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes: >I have seen too many Pascal programs where people wrote > x: boolean; ... if x = TRUE then ... > ... I _have_ seen it in C code, and it was wrong. I don't want people who do that kind of thing hacking my code. If they get something as simple as this wrong then what hope is there of them getting anything else right? >Why should your proposed C variant be more restrictive than Pascal? >In Pascal FALSE < TRUE is allowed, meaningful, and true. I can't see any use for this, but I don't have any violent objection to it. I'd make a couple of changes to my scheme on reflection: I'd disallow assignment of 0 and 1 to bool variables, and make TRUE and FALSE keywords. I'd also turn on a macro __BOOL__ to indicate that the extension was there, so that applications could say: #ifndef __BOOL__ typedef int bool; #define TRUE 1 #define FALSE 0 #endif Mark
chip@tct.uucp (Chip Salzenberg) (09/05/90)
According to flint@gistdev.gist.com (Flint Pellett): >Truth values may only be stored away in some database that is read by a >different program. If the different program is C right now, you'll >define TRUE=1 and FALSE=0. Next year, you may rewrite the different >program in some other language where TRUE is 0, and FALSE is 1 ... In such a case, the values stored in the database should best be named DB_TRUE and DB_FALSE, with assignment such: db_boolean = boolean ? DB_TRUE : DB_FALSE; External data representation is irrelevant to the TRUE/FALSE issue. -- Chip Salzenberg at Teltronics/TCT <chip@tct.uucp>, <uunet!pdn!tct!chip>
karl@haddock.ima.isc.com (Karl Heuer) (09/06/90)
[Lots of people have said:] >Writing `if (b == TRUE)' is wrong. No. It's correct but silly (as is `if ((x < y) == TRUE)'). The flaw is in writing `b = isdigit(ch)', because `isdigit()', despite the misleading name, is *not* a Boolean function. If this had been done right, the function would be guaranteed to return the normalized truth value, namely 1, on success. (Fixed in INTERACTIVE Unix, incidentally. It's no less efficient under normal usage, and less error-prone under abnormal usage.) In article <3686@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes: >Let's not forget the Lisp Lesson: there is often something useful you >could return instead of an anonymous "true". Fine; functions that have that property can be declared with a type other than "bool". Functions that don't have any useful value to return on success can and should return TRUE. (fgets() was another mistake.) >Why should your proposed C variant be more restrictive than Pascal? >In Pascal FALSE < TRUE is allowed, meaningful, and true. For the same reason that it's more restrictive than current C. The relation b1 < b2 isn't sufficiently useful to be worth supporting, IMHO (since it's equivalent to !b1 && b2); and forbidding it may make it possible to catch real mistakes. In article <898@mwtech.UUCP> martin@mwtech.UUCP (Martin Weitzel) writes: >K&R tried to make this very easy with C ... they LEFT OUT out a >"boolean" or "logical" datatype and gave *very simple* rules for >using int for that purpose. Hey! They could have made it *even simpler*, by making `int' and `float' the same type, like in BASIC or APL! (Sarcasm alert.) Conceptually, C already has booleans: the operands of various control-flow statements and operators; the results of relationals, etc. The usual description of the language is in terms of `int', but it would be equally correct to say that `a < b' returns an object of type Boolean, and that Booleans and integers are implicitly converted by the rules `i=(b?1:0)' and `b=(i!=0)', and that there is no keyword for Boolean (and hence one may not declare objects of that type, but must instead fall back on int). >And how have you made sure that the readers have grasped *your* strict rules >when and when not to use TRUE and FALSE and will apply it correctly? It's not as if everyone is making up their own rules. Not mixing Boolean and integer data should simply an `obvious' rule, like not mixing pointers and ints (even if you happen to be using a language that's sloppy about it--C in the first case, BCPL in the second). >[Why not use the names SET/CLEAR, YES/NO, ON/OFF, SUCCESS/FAILURE instead]? >Is it because most of you (still) grew up with PASCAL? Probably. I use YES/NO myself, following Kernighan I believe, but I've noticed it's a minority position. If <bool.h> were standardized, I'd switch to whatever it used. >And a final word to the ones who advocate "strong typing": You should >clearly vote for differently typed truth values: If you are reluctant when >it comes to mixing (integer) numbers with truth values, you should >clearly vote for *differently named* constants for different sorts >of truth values. This is true to some extent (and applies to other types as well as Booleans), but (a) mixing apples and oranges is sometimes the right thing (how do you do a matrix multiply if you've typedef'd `row_index' and `col_index'?), and (b) at some point it just becomes too much work to declare a new type for an object that has a small scope. (Though if the compiler enforced the type-mixing restriction, it might be worthwhile.) Karl W. Z. Heuer (karl@kelp.ima.isc.com or ima!kelp!karl), The Walking Lint
olu@sysauto.UUCP (Olumide O. Emuleomo) (09/06/90)
In article <2341@cirrusl.UUCP>, dhesi%cirrusl@oliveb.ATC.olivetti.com (Rahul Dhesi) writes: > I was brash enough to suggest > > But in fact my suggested definitions are much more useful than the > common recommendation of defining TRUE and FALSE. > > No matter how careful the original programmer is, sooner or later > somebody will come along and, noticing that TRUE means true, will try > to do something like: > > i = isdigit(c); > ... > if (i == TRUE) ... > > Of course, defining ONE and ZERO is fairly silly. But defining TRUE > and FALSE is quite risky. I'll take fairly silly over quite risky any > day. > -- > Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com> > UUCP: oliveb!cirrusl!dhesi TRUE and FALSE were never meant to be tested explicitly. Good programming practise demands that they be used somewhat as follows if( keyword_found(fname) ) { etc... etc... etc.. } keyword_found(char *fname_param) { switch(foo) { case FAILURE: etc... return(FALSE); case SQLNOTFOUND: more etc.. return(FALSE); etc... } return(TRUE); } Cheers --olu@sysauto.UUCP
george@hls0.hls.oz (George Turczynski) (09/06/90)
In article <F4u9._?1@cs.psu.edu>, flee@guardian.cs.psu.edu (Felix Lee) writes: > Personally, I use this set of macros: > #define FALSE 0 > #define CNAND(a,b) (!((a)&&(b))) > #define CNOT(a) CNAND(a,a) > #define CXOR(a,b) CNAND(CNAND(a,CNOT(b)),CNAND(b,CNOT(a))) > #define CEQUIV(a,b) CNOT(CXOR(a,b)) > #define COR(a,b) CEQUIV(a,CNAND(CNOT(a),CXOR(a,b))) > #define CAND(a,b) CXOR(COR(a,b),CXOR(a,b)) > #define TRUE COR(FALSE,CNOT(FALSE)) > #define ISTRUE(a) CAND(TRUE,a) > #define ISFALSE(a) CNOT(ISTRUE(a)) > > These may unfortunately overrun some compiler or preprocessor limits > (TRUE expands to an 853 character expression, and ISFALSE(x) expands > to 203677 characters). But they're otherwise quite portable, and I > find the prefix style much more readable than C's cryptic infix > expressions, especially when used in conjunction with a set of macros > that provide LISP-ish control structures. I don't think you'll find many compilers that are happy with those extremely inefficient macros. Whether or not it compiles is one thing, but how long it takes is another ! I think that anything that expands to a 200,000 oharacter or so long expression is ridiculous, but that's my opinion :-) Let me ask why you chose to use "!" and "&&" and ignored "||" ? Do you not trust it or something ? It is just as valid in C as "!" and "&&" ! It is not clever to create such nonsensical macros. Why not try these: #define FALSE 0 #define CNOT(a) (!(a)) #define CAND(a,b) ((a)&&(b)) #define CNAND(a,b) CNOT(CAND(a,b)) #define COR(a,b) ((a)||(b)) #define CNOR(a,b) CNOT(COR(a,b)) #define CXOR(a,b) CNOR(CAND(a,b),CNOR(a,b)) #define CXNOR(a,b) COR(CAND(a,b),CNOR(a,b)) #define CEQUIV(a,b) CXNOR(a,b) #define TRUE CNOT(FALSE) #define ISTRUE(a) CAND(a,TRUE) #define ISFALSE(a) CNOT(ISTRUE(a)) I too have used but one of each of "0", "!", "&&", and "||" as well. Now, I can show you what these expand to, because they aren't ridiculously long: 0 (!(a)) ((a)&&(b)) (!(((a)&&(b)))) ((a)||(b)) (!(((a)||(b)))) (!(((((a)&&(b)))||((!(((a)||(b)))))))) ((((a)&&(b)))||((!(((a)||(b)))))) ((((a)&&(b)))||((!(((a)||(b)))))) (!(0)) ((a)&&((!(0)))) (!(((a)&&((!(0)))))) respectively. Now, if anyone feels that they MUST use macros like these, use mine, or even better, make your own up not defining them in terms of each other, but in terms of "0", "!", "&&" and "||" (This will remove any pairs of redundant parentheses). Have a nice day... -- | George P. J. Turczynski. |---------------------------------------------------- | Computer Systems Engineer. | ACSnet: george@highland.oz | I can't speak for the | | Highland Logic Pty. Ltd. | Phone: +61 48 683490 | company, I can barely | | Suite 1, 348-354 Argyle St | Fax: +61 48 683474 | speak for myself... | | Moss Vale. NSW. Australia. 2577 |----------------------------------------------------
userAKDU@mts.ucs.UAlberta.CA (Al Dunbar) (09/06/90)
In article <23970:Sep505:16:2390@kramden.acf.nyu.edu>, brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: >In article <5398@harrier.ukc.ac.uk> mtr@ukc.ac.uk (M.T.Russell) writes: >> To the people complaining about `if (x == TRUE)': YOU ONLY USE `TRUE' AND >> `FALSE' FOR ASSIGNMENT AND PARAMETER PASSING. It's a fairly simple rule. > >Usage: truefalse flagfoo; set_true(&flagfoo); if (is_true(flagfoo)) ... ARGH! the "is_true" is understood, and redundant ! 'if (flagfoo)' means 'if flagfoo is true', in the same way that the following two sentences mean (almost) exactly the same thing in English: 1) If it is raining you will get wet. 2) If it is true that it is raining you will get wet. The is_true() macro is almost as useful as a macro you might write to return the numeric value of its argument, i.e.: a = value_of( b ) + value_of( c ); -------------------+------------------------------------------- Al Dunbar | Edmonton, Alberta | this space for rent CANADA | -------------------+-------------------------------------------
jar@ifi.uio.no (Jo Are Rosland) (09/06/90)
In article <3686@goanna.cs.rmit.oz.au>, ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes: > Let's not forget the Lisp Lesson: there is often something useful you > could return instead of an anonymous "true". For example, if you are > looking for a character in a string, instead of returning TRUE (it is > there) or FALSE (it isn't) you could return a pointer to if it it's > there or NULL if it isn't. If you program with that in mind, you really > don't _care_ much about TRUE/FALSE -vs- 1/0, because it's rare that you > have so little to say. Exactly! Some examples of this from Lisp, are: (or a b) (and a b) which evaluate to either nil, or the value of a or b. Why can't I do this in C? The && and || operators are defined to return either 0 or 1. With the Lisp semantics I could write code like: return f() || g(); instead of: a = f(); if (a) return a; else return g(); This is useful eg. when f() and g() return pointers (maybe in some sort of search, where we only want to call the second function if the first one fails.) And I even think the first version reads better. So why does && and || return 0 or 1? What would break if they were less restrictive? Operators like <, >, ==, which are most often used in conjunction with && and ||, return 0 or 1 anyway. -- _____________________ |/ Jo Are Rosland [) joare@sdata.no
fmcwilli@oracle.oracle.com (Floyd McWilliams) (09/06/90)
In article <F4u9._?1@cs.psu.edu> flee@guardian.cs.psu.edu (Felix Lee) writes: |Personally, I use this set of macros: |#define FALSE 0 |#define CNAND(a,b) (!((a)&&(b))) |#define CNOT(a) CNAND(a,a) |#define CXOR(a,b) CNAND(CNAND(a,CNOT(b)),CNAND(b,CNOT(a))) |#define CEQUIV(a,b) CNOT(CXOR(a,b)) |#define COR(a,b) CEQUIV(a,CNAND(CNOT(a),CXOR(a,b))) |#define CAND(a,b) CXOR(COR(a,b),CXOR(a,b)) |#define TRUE COR(FALSE,CNOT(FALSE)) |#define ISTRUE(a) CAND(TRUE,a) |#define ISFALSE(a) CNOT(ISTRUE(a)) |These may unfortunately overrun some compiler or preprocessor limits |(TRUE expands to an 853 character expression, and ISFALSE(x) expands |to 203677 characters). But they're otherwise quite portable, and I |find the prefix style much more readable than C's cryptic infix |expressions, especially when used in conjunction with a set of macros |that provide LISP-ish control structures. Plus, the definitions are intuitively obvious! One can only pity the programmers who use such twisted constructs as if (!a) /* If not a */ if (a || b) /* If a or b */ if (a && b) /* If a and b */ and the unthinkable if (a) /* If a is true */ p.s. Felix, while it's true that TRUE is FALSE OR NOT FALSE, it also happens to be NOT FALSE AND NOT FALSE. So for _maximum_portability_, you should have #define TRUE CAND(COR(FALSE,CNOT(FALSE)),CAND(CNOT(FALSE),CNOT(FALSE))) -- Floyd McWilliams -- fmcwilli@oracle.com "I don't mind anybody dropping out of anything, but it's the imposition on somebody else I don't like. The moment you start dropping out and then begging off somebody else to help you, then it's no good." -- George Harrison
staff@cadlab.sublink.ORG (Alex Martelli) (09/07/90)
mtr@ukc.ac.uk (M.T.Russell) writes: >In article <3686@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes: ...omitted... >>In Pascal FALSE < TRUE is allowed, meaningful, and true. >I can't see any use for this, but I don't have any violent objection An use is to implement the "MATERIAL IMPLICATION" operator of propositional calculus; "a IMPLIES b" can be written "a <= b" under Pascal's ordering (yup, as another poster points out, it can also be written "b OR NOT a", but then why have an AND operator at all, since it "a AND b" can be easily rewritten "NOT (NOT a OR NOT b)"...?-). -- Alex Martelli - CAD.LAB s.p.a., v. Stalingrado 45, Bologna, Italia Email: (work:) staff@cadlab.sublink.org, (home:) alex@am.sublink.org Phone: (work:) ++39 (51) 371099, (home:) ++39 (51) 250434; Fax: ++39 (51) 366964 (work only; any time of day or night).
mcdaniel@adi.com (Tim McDaniel) (09/07/90)
In article <F4u9._?1@cs.psu.edu> flee@guardian.cs.psu.edu (Felix Lee) writes: > Personally, I use this set of macros: > #define FALSE 0 > #define CNAND(a,b) (!((a)&&(b))) > #define CNOT(a) CNAND(a,a) > #define CXOR(a,b) CNAND(CNAND(a,CNOT(b)),CNAND(b,CNOT(a))) > ... > These may unfortunately overrun some compiler or preprocessor limits > (TRUE expands to an 853 character expression, and ISFALSE(x) expands > to 203677 characters). I called each macro once and printed the resulting lengths (assuming a character-based preprocessor). It took about 90 CPU seconds to compile on a Sun 3/60 with a local hard disk. Macro call Length Macro call Length FALSE 1 COR(x,y) 637 CNAND(x,y) 13 CAND(x,y) 2149 CNOT(x) 13 TRUE 853 CXOR(x,y) 61 ISTRUE(x) 101833 CEQUIV(x,y) 133 ISFALSE(x) 203677 ISTRUE generates so many characters because it uses CAND, which passes its first argument (here, TRUE) to both COR and CXOR; COR passes three copies to CEQUIV, CNOT, and CXOR, respectively; et cetera. > especially when used in conjunction with a set of macros that > provide LISP-ish control structures. I haven't found a good way to do LAMBDA -- any suggestions? With 'm4', of course, it's trivial. I rather like the way I did CONS. I didn't want the overhead of checking for free space and allocating more on every call. Basically, it increments the free-store pointer and just dereferences it. If it's a bad pointer now, a signal handler does an "sbrk" to get a lot more space, and it restarts the instruction that failed. And it's so portable -- at least, it worked on every VAX that I tried it on. Have I mentioned dereferencing NIL yet, to get 0? By the way, would anyone like a copy of the shell I wrote in this LISPish C? I call it "The Still-Bourne Shell". -- Tim McDaniel Applied Dynamics Int'l.; Ann Arbor, Michigan, USA Work phone: +313 973 1300 Home phone: +313 677 4386 Internet: mcdaniel@adi.com UUCP: {uunet,sharkey}!amara!mcdaniel
brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (09/07/90)
In article <1308@mts.ucs.UAlberta.CA> userAKDU@mts.ucs.UAlberta.CA (Al Dunbar) writes: > In article <23970:Sep505:16:2390@kramden.acf.nyu.edu>, brnstnd@kramden.acf.nyu.edu (Dan Bernstein) writes: > > In article <5398@harrier.ukc.ac.uk> mtr@ukc.ac.uk (M.T.Russell) writes: > > > To the people complaining about `if (x == TRUE)': YOU ONLY USE `TRUE' AND > > > `FALSE' FOR ASSIGNMENT AND PARAMETER PASSING. It's a fairly simple rule. [ I point out how you can enforce this with syntax, if you're desperate ] > ARGH! the "is_true" is understood, and redundant ! Even if you were right, that would be irrelevant ! The point of this discussion is to please Pascal programmers. Once again, the following should satisfy them: typedef struct { int truth; } truefalse; #define set_true(b) ((void) ((b)->true = 1)) #define set_false(b) ((void) ((b)->true = 0)) #define is_true(b) ((b).true) Usage: truefalse flagfoo; set_true(&flagfoo); if (is_true(flagfoo)) ... Notice that if (flagfoo) is illegal, and there is no possible problem along the lines of if (flagfoo == TRUE). Unless I'm mistaken, this is what the Pascalites are looking for. (Isn't it?) ---Dan
jimmy@tybalt.caltech.edu (Jimmy Hu) (09/07/90)
In article <5409@harrier.ukc.ac.uk> mtr@ukc.ac.uk (M.T.Russell) writes: > >I'd make a couple of changes to my scheme on reflection: I'd disallow >assignment of 0 and 1 to bool variables, and make TRUE and FALSE keywords. >I'd also turn on a macro __BOOL__ to indicate that the extension was >there, so that applications could say: > > #ifndef __BOOL__ > typedef int bool; > #define TRUE 1 > #define FALSE 0 > #endif There only purposes for a boolean type in C are for "increased readability" and "stronger type-checking." But for me, personally, the reason I use C instead of say, Pascal, is that I don't want all these restrictions. I don't want to hide the fact that the "booleans" I use are actually ints. I LIKE the fact that C's "booleans" are ints, and I use that fact when I write something like: d = (a==b)*c; /* Just a contrived example to set d to 0 or c */ I like the fact that all nonzero values are considered true. For instance, when I have an int in which I may or may not have set certain bits, and I want to know if I have set any, I can just write "if (flags)" and not "if (flags != 0)." Or if I want to check a certain bit, I can write "if (flags & 0x0800)" and not "if ((flags & 0x0800) != 0)." Now I have no objection to people who use TRUE and FALSE for better readability as long as they know what they're doing. And if they want to construct a boolean type with bool.h, that's also fine with me. But as for the idea of making a standard boolean type for C, I'd have to say that this is going a bit too far, since it would make life difficult to the many programmers who actually make good use of ints as booleans. Since most languages compile booleans to ints anyways, why not use that extra space for your own purposes? About the macros that put out a 200000+ character definition, I think that this is just plain silly. Most of those definitions could have been shortened by defining them on C's operators instead of on each other. Why make the compiler spend minutes deciphering "ISFALSE(x)" and substituting a monstrously convoluted synonym for (!(x))? I mean, this is C, so use it to your full advantage, right? You don't have to use the more restrictive Pascal-like or the more verbose Lisp-like constructs! I think other C programmers looking at your code will understand what you mean even when you use obscure things like "if (x)" instead of "if ISTRUE(x)." :-) -- ---------------------------------------------------------------------- Jimmy Hu jimmy@tybalt.caltech.edu ----------------------------------------------------------------------
stephen@estragon.uchicago.edu (Stephen P Spackman) (09/07/90)
To pick up on a couple of comments: In article <1990Sep6.113259.2109@ifi.uio.no> jar@ifi.uio.no (Jo Are Rosland) writes: Why can't I do this in C? The && and || operators are defined to return either 0 or 1. With the Lisp semantics I could write code like: return f() || g(); instead of: a = f(); if (a) return a; else return g(); This is useful eg. when f() and g() return pointers (maybe in some sort of search, where we only want to call the second function if the first one fails.) And I even think the first version reads better. The single most important case is, of course, when f() and g() return functions. You could code all of that ecchy high-level strategy-finding code in this style: return (f() || g())(x); And if THAT ain't clarity, I don't know what is. In article <1990Sep6.113259.2109@ifi.uio.no> jar@ifi.uio.no (Jo Are Rosland) writes: The is_true() macro is almost as useful as a macro you might write to return the numeric value of its argument, i.e.: a = value_of( b ) + value_of( c ); I'm in favour, though I'd like to see a prefix operator for this. There damn well ought to be an operator for extracting values from variables, so people don't confuse them with constants. And to encourage them to USE constants. (And it's been done in real languages, honest). stephen p spackman stephen@estragon.uchicago.edu 312.702.3982
simons@tetrauk.UUCP (Simon Shaw) (09/07/90)
In article <926@hls0.hls.oz> george@hls0.hls.oz (George Turczynski) writes: >In article <F4u9._?1@cs.psu.edu>, flee@guardian.cs.psu.edu (Felix Lee) writes: > >> Personally, I use this set of macros: >> #define CNAND(a,b) (!((a)&&(b))) >> #define CNOT(a) CNAND(a,a) >> #define CXOR(a,b) CNAND(CNAND(a,CNOT(b)),CNAND(b,CNOT(a))) > >Why not try these: > >#define CNOT(a) (!(a)) >#define CAND(a,b) ((a)&&(b)) >#define COR(a,b) ((a)||(b)) >#define CNOR(a,b) CNOT(COR(a,b)) >#define CXOR(a,b) CNOR(CAND(a,b),CNOR(a,b)) If because the value returned by an expression of the form (a == b) is _defined_ to be 0 or 1, we are defining TRUE as 1 and ZERO as 0 (no contention yet, I hope), then the worst of these expansions, XOR, should be contracted by the use of the bitwise XOR. #define CXOR(a,b) ((a)^(b)) with this assumption (TRUE==1, FALSE==0), are there any circumstances where the use of the bitwise operator would be unsafe ? IMHO, the compounds NOR and NAND, while much more useful if constructing electronic circuitry, are considerably less clear in code (Not to mention XNOR, which even now takes me a few seconds to work out). Lets not forget the poor sods who've got to maintain this code ! Anyone disagree ? -- Simon Shaw ; simons@tetrauk.uucp
chip@tct.uucp (Chip Salzenberg) (09/07/90)
According to joare@sdata.no: > return f() || g(); Perl also has the LISP meaning for this operation. This C equivalent is too verbose: > a = f(); > if (a) > return a; > else > return g(); Try instead: return (a = f()) ? a : g(); Note that GNU CC has a ?: operator where the second operand defaults to the value of the first operand. So with GNU CC, you can write: return f() ?: g(); Followups to alt.lang.cfutures. -- Chip Salzenberg at Teltronics/TCT <chip@tct.uucp>, <uunet!pdn!tct!chip>
jcl@bdrc.bd.com (John C. Lusth) (09/07/90)
Rahul Dhesi writes:
<When I find somebody who really, really, really wants to define TRUE
<and FALSE, even somebody who uses them for assignment only, I recommend
<the following defines instead:
<
< #define ZERO 0
< #define ONE 1
<
<These are so much more clear than TRUE and FALSE, and if you use
<them in a test, you know exactly what you're testing!
Concerning the various disparaging remarks about the stupidity
of the above defines:
Jeez, guys, don't you know sarcasm when you see it? Defining
TRUE and FALSE in C is just about as stupid as defining ZERO and ONE.
john
--
John C. Lusth, Becton Dickinson Research Center, RTP, NC, jcl@bdrc.bd.com
ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (09/09/90)
>In article <F4u9._?1@cs.psu.edu>, flee@guardian.cs.psu.edu (Felix Lee) writes: > Personally, I use this set of macros: > #define CNAND(a,b) (!((a)&&(b))) Surely this was a joke? In article <728@tetrauk.UUCP>, simons@tetrauk.UUCP (Simon Shaw) writes: > IMHO, the compounds NOR and NAND, while much more useful if constructing > electronic circuitry, are considerably less clear in code (Not to mention > XNOR, which even now takes me a few seconds to work out). What's in a name? XNOR(a,b) is just !!(a) == !!(b), true when a and b have the same "truth value", false when they differ. It's available in Fortran under the name .EQV., in Algol 60 under the name %equiv, and in Pascal and Ada under the name '='. Rename XNOR() to BoolEqual() and it is pretty easy to figure out. -- Psychiatry is not about cure. Psychiatry is about power.
george@hls0.hls.oz (George Turczynski) (09/11/90)
In article <728@tetrauk.UUCP>, simons@tetrauk.UUCP (Simon Shaw) writes: > In article <926@hls0.hls.oz> george@hls0.hls.oz (George Turczynski) writes: > >Why not try these: > > > >#define CNOT(a) (!(a)) > >#define CAND(a,b) ((a)&&(b)) > >#define COR(a,b) ((a)||(b)) > >#define CNOR(a,b) CNOT(COR(a,b)) > >#define CXOR(a,b) CNOR(CAND(a,b),CNOR(a,b)) > > If because the value returned by an expression of the form (a == b) > is _defined_ to be 0 or 1, we are defining TRUE as 1 and ZERO as 0 > (no contention yet, I hope), then the worst of these expansions, > XOR, should be contracted by the use of the bitwise XOR. > > #define CXOR(a,b) ((a)^(b)) No, I think not. These macros all return a result that is TRUE or FALSE. The bitwise XOR operator will not always produce the result TRUE or FALSE. These macros are not performing bitwise arithmetic, but logical arithmetic. > with this assumption (TRUE==1, FALSE==0), are there any circumstances > where the use of the bitwise operator would be unsafe ? In this case, yes. If someone were to say:- if( CXOR(a,b) == TRUE ) they would in most cases be in trouble, if the XOR operator was used. Now, you and I both know that few people would construct it like that, but that is beside the point, these macros ALWAYS result in TRUE or FALSE. > IMHO, the compounds NOR and NAND, while much more useful if constructing > electronic circuitry, are considerably less clear in code (Not to mention > XNOR, which even now takes me a few seconds to work out). Lets not > forget the poor sods who've got to maintain this code ! > > Anyone disagree ? Don't think for one second that I would entertain the idea of using these macros ! Felix Lee posted his set, some of which expanded to 200,000++ characters. Thinking this was ridiculous, and that I might save some people wasting too much of their time, I posted the same (but improved) macro set, that didn't waste so much space. I agree, don't waste your time with the above macros. (But Felix Lee could at least use the sensible ones, not the 200,000++ char ones :-}) -- | George P. J. Turczynski. |---------------------------------------------------- | Computer Systems Engineer. | ACSnet: george@highland.oz | I can't speak for the | | Highland Logic Pty. Ltd. | Phone: +61 48 683490 | company, I can barely | | Suite 1, 348-354 Argyle St | Fax: +61 48 683474 | speak for myself... | | Moss Vale. NSW. Australia. 2577 |----------------------------------------------------
mcdaniel@adi.com (Tim McDaniel) (09/11/90)
Ye gods, folks, get a clue! george@hls0.hls.oz (George Turczynski) writes: Don't think for one second that I would entertain the idea of using these macros! Felix Lee posted his set, some of which expanded to 200,000++ characters. Thinking this was ridiculous, and that I might save some people wasting too much of their time, I posted the same (but improved) macro set, that didn't waste so much space. Felix Lee was indeed being ridiculous -- deliberately. It is called "satire", a subclass of the class "joke". Specifically, he was "making fun" of the whole TRUE/FALSE definition topic. Trying to "optimize" those macros was itself a complete waste of time. My own contribution was > > I rather like the way I did CONS. I didn't want the overhead of > checking for free space and allocating more on every call. Basically, > it increments the free-store pointer and just dereferences it. If > it's a bad pointer now, a signal handler does an "sbrk" to get a lot > more space, and it restarts the instruction that failed. And it's so > portable -- at least, it worked on every VAX that I tried it on. > > Have I mentioned dereferencing NIL yet, to get 0? > > By the way, would anyone like a copy of the shell I wrote in this > LISPish C? I call it "The Still-Bourne Shell". NOTE: THESE WERE ALSO "JOKES", as may be inferred from the "portability == VAX" line. Among other things, signal handlers on some machines cannot or do not restart the instruction that failed. Also, "(char *) 0" can be dereferenced in a *few* operating systems to get '\0', but on most architectures, it causes a fatal error or fetches garbage. (See the "Frequently-Asked Questions" list.) The original Bourne shell (called /bin/sh on most machines) was written in pseudo-Algolish C, and used the "sbrk" kludge to do memory allocation. It was a maintenance nightmare and ridiculously unportable, and basically had to be rewritten. -- Tim McDaniel Applied Dynamics Int'l.; Ann Arbor, Michigan, USA Work phone: +313 973 1300 Home phone: +313 677 4386 Internet: mcdaniel@adi.com UUCP: {uunet,sharkey}!amara!mcdaniel
amull@Morgan.COM (Andrew P. Mullhaupt) (09/13/90)
In article <MCDANIEL.90Sep11124417@dolphin.adi.com>, mcdaniel@adi.com (Tim McDaniel) writes: | Ye gods, folks, get a clue! | | george@hls0.hls.oz (George Turczynski) writes: | | Don't think for one second that I would entertain the idea of using | these macros! Felix Lee posted his set, some of which expanded to | 200,000++ characters. Thinking this was ridiculous, and that I | might save some people wasting too much of their time, I posted the | same (but improved) macro set, that didn't waste so much space. | | Felix Lee was indeed being ridiculous -- deliberately. It is called | "satire", a subclass of the class "joke". Specifically, he was | "making fun" of the whole TRUE/FALSE definition topic. Trying to | "optimize" those macros was itself a complete waste of time. I don't think so. I think I could get them to expand to a Meg if I thought about it...one of the funniest guys I ever knew was Steve Pomerance, who had perfected the technique (which I still call Pomerancing) of pretending to completely miss the point of your joke until after you had explained it at least three different ways. You should meet him. Later, Andrew Mullhaupt | NOTE: THESE WERE ALSO "JOKES" You said it.
george@hls0.hls.oz (George Turczynski) (09/17/90)
In article <MCDANIEL.90Sep11124417@dolphin.adi.com>, mcdaniel@adi.com (Tim McDaniel) writes: > Ye gods, folks, get a clue! ... > > Felix Lee was indeed being ridiculous -- deliberately. It is called > "satire", a subclass of the class "joke". Specifically, he was > "making fun" of the whole TRUE/FALSE definition topic. Trying to > "optimize" those macros was itself a complete waste of time. ... With all the nonsensical things that people post (some of which ARE in earnest) one just can't tell what is foolishness and what is satire. Some of it can't be very good satire, else certain people wouldn't mistake it for foolishness :-) If all such articles were taken as `satire', many people would go on blindly using their sometimes questionable ideas. Where would that leave them ? I will, in future, be on the lookout for such satirical articles :-) -- | George P. J. Turczynski. |---------------------------------------------------- | Computer Systems Engineer. | ACSnet: george@highland.oz | I can't speak for the | | Highland Logic Pty. Ltd. | Phone: +61 48 683490 | company, I can barely | | Suite 1, 348-354 Argyle St | Fax: +61 48 683474 | speak for myself... | | Moss Vale. NSW. Australia. 2577 |----------------------------------------------------
mautner@odin.ucsd.edu (Craig Mautner) (09/25/90)
The author of this does not have access to the news groups. He asked me to post this and see what comments it generates. Any correspondence should be sent to him at the internet address included in the header. -Craig Mautner //////////////////// Begin Included Message //////////////////////// Seven Original Sins of K&R by Philip J. Erdelsky Compuserve: 75746,3411 Internet: 75746.3411@compuserve.com September 22, 1990 The creation of C approximately two decades ago was a wondrous event, even if it did not seem so at the time. Like all human creations, C was imperfect. I have identified seven Original Sins--minor flaws in C for which K&R will eventually have to answer, in this world or the next. I call them original sins because they were present when C originated, not because K&R were the first to commit them. Some of these sins have been purged from later versions of C, but others remain with us. I am not the first to decry these sins, nor will I be the last. I am merely another in a long series of prophets crying in the wilderness. I The First Original Sin was pitifully weak typing. There is no Boolean type in C, so generations of programmers have erroneously written something like "if (x=5)" instead of "if (x==5)", only to wonder why x always seems to be 5, regardless of what has gone before. The "char" type was not specified as either signed or unsigned. This sin has probably wasted more CPU time than any other, as savvy programmers learn to put a defensive "&0xFF" after every "char" expression that needs to be unsigned. The default type for functions should have been "void", not "int", but there was originally no "void" type. Modern compilers have provided partial redemption from this sin, usually by issuing warning messages when the program appears to be tainted. But these warnings are often false alarms and go unheeded. There is still no Boolean type, and "char" may be either signed or unsigned. Even the new enumeration types are merely integers in disguise, just as willing to be mixed as matched. II The Second Original Sin was the failure to make "NULL" a keyword. Beginning C programmers wonder why you have to "#include <stdio.h>" in a program that doesn't use standard I/O. Some compilers don't even object when you assign an integer constant to a pointer without a typecast, especially when the constant happens to be zero. Don't blame the compiler. The poor thing can't tell the difference between a zero integer constant and "NULL". Redemption from this sin is on its way. Modern compilers define "NULL" as "(void *) 0", so there's at least some hope of distinguishing it from a plain old zero. III The Third Original Sin was the use of the keyword "static" to mark a function or variable as local to particular source file. This is really a trinity of sins. The word "static" doesn't mean local. It conflicts with the other use of the word "static"--to mark a variable inside a function as one that actually is static, in an accepted meaning of the word. Finally, even if the word "local" had been used instead, it would have been marking the wrong thing. The word "public", or some similar word, should have been used to mark the few functions and variables that must be made available to the code in other files. Other functions and variables should have been local by default. That's how it's done in assembly language and other high-level languages, and the reason for it is obvious. From this sin, however, no redemption is in sight. IV The Fourth Original Sin is the mandatory use of the "break" keyword to terminate a "case" clause in a "switch" statement. Omitting it is natural for beginning programmers, and sometimes even for experienced programmers who have been dabbling in more tightly structured languages. Of course, this causes control to fall through to the next case, which is occasionally useful but nearly always a mistake, like a double exposure in photography. But the evil goes even further. Often, the "switch" statement is enclosed in a "for" or "while" loop. You want to finish up a "case" clause by breaking out of the loop? You can't do it in C, not without breaking out of the "switch" statement first! The solution, not likely to be adopted even in C+++, would be to have the compiler put an implicit "break" at the end of every "case" clause, and reserve the "break" keyword for breaking out of loops, the way God intended. V The Fifth Original Sin was the way functions are defined. The entire parameter list has to be written twice. That's something no programmer should have to do unless it's absolutely necessary. And to compound the evil, an untyped parameter defaults to type "int". Most programmers have written something like "strcmp(s,t)", forgetting the declaration "char *s,*t;". What you wind up with in most cases is, not a function that fails, but something worse--a function that works as long as pointers and integers are the same size, and then fails when you try to port it. Fortunately, ANSI C permits prototype definitions, but the old way is still permitted, at least during a transitional period. Let's hope the transition is brief. VI The Sixth Original Sin was the way conflicts among the names of members of different structures were neither forbidden nor resolved. The original K&R said that different structures could have members with identical names as long as they had identical offsets. The way early compilers implemented this dictum varied. Some compilers would check to see that the offsets were indeed identical. Others simply generated erroneous code when they weren't. Most programmers took the safest course by including the structure name--usually abbreviated--in every member name. Modern compilers have atoned for this sin completely by keeping a separate member list for each structure type. This resolves the conflicts, but a reminder of past iniquities persists in the awkward names of structure members in UNIX source code and other old C scriptures. VII The Seventh Original Sin was the eight-character limit on distinguishable names, or even fewer than eight for externally defined names. Of course, some such limitation was required for efficient implementation, but eight characters are not enough. C was much better than Fortran, which allowed only six, but there are many pairs of English words with distinct meanings whose first eight letters are identical. The minimum number depends on the language, but for English about 20 should be sufficient. German programmers need more. Most modern compilers do have a reasonable limit, but some compiler developers have apparently forgotten that virtue lies in moderation. One compiler allows at least several hundred characters, maybe more. That's too long. Compilers are supposed to compile, not test the limits of computability by allowing single labels to occupy practically the entire computer memory (and disk swap area). An unprintable name--one that won't fit on a single line--should also be uncompilable. Epilogue None of these sins is inconsistent with the philosophy of C. We needn't embrace heresies like Pascal, Modula 2 or Ada. But we must abandon the false god of 100% upward compatibility. We must tear down the old temple to build a new one. Then, and only then, will our redemption be at hand. Note This jeremiad is not copyrighted. You are welcome to copy it and pass it on. I only ask you to leave my name and account number on it. Let me take the credit--and the heat. //////////////////// End Included Message //////////////////////// -- -------------------------------------------------------------------- Craig D. Mautner UCSD mautner@cs.ucsd.edu Dept of CSE, C-014 (619) 534-4526 La Jolla, Ca. 92093
ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) (09/26/90)
In article <12777@sdcc6.ucsd.edu>, mautner@odin.ucsd.edu (Craig Mautner) writes: [1. "Weak typing" was the phrase used, but actually ; no "bool" type, and ; char neither signed nor unsigned were the real complaints.] In the beginning there was CPL, an Algolish language with lots of goodies including types. CPL begat BCPL, which was the nearest thing there was to a portable "implementation language" for years. However, BCPL had one type: "machine word", which had to serve for integer, float, and (word) address. BCPL begat B, and B begat C, which broke with years of BCPL/B tradition by adding types (it took a couple of revisions, but they're there). 'char' was neither signed nor unsigned because the machines of the time didn't support both well; on a PDP-11 unsigned char was much more costly than signed char, while on an IBM/360 signed char was much more costly than signed char. The one thing you were guaranteed was that any character in the machine's usual alphabet could be represented in a 'char'. This is not a case of weak typing: this implementation freedom in the representation of characters was excellent engineering. That there was no _separate_ type for "8 bit integer", _that_ is the weak point. But there weren't originally any unsigned integers either. (In UNIX V6 it was still common to use pointer arithmetic to get unsigned integer arithmetic.) It may sound as though I am nit-picking, but there's really no point in raving on about the faults of a programming language unless you get it quite clear what the faults really are. Something which has long puzzled me is why so much of BCPL was retained in C, yet MANIFEST was not. In BCPL you could say MANIFEST $( I = 1, J = 2, K = 3 $) which corresponds to ANSI C const int i = 1, j = 2, k = 3; and as in ANSI C the "initial values" could be expressions. Oh well, what was that about repeating history? [2. NULL not a keyword] > Beginning C programmers wonder why you have to "#include <stdio.h>" > in a program that doesn't use standard I/O. Any beginning C programmer who wonders any such thing has been taught shockingly badly. You *don't* have to include stdio.h. In ANSI C, NULL is defined in at least three places: stddef.h NULL, offsetof, ptrdiff_t, size_t, wchar_t stdlib.h NULL, EXIT_SUCCESS, EXIT_FAILURE, lots of fns stdio.h NULL, FILE, fopen, ... In pre-ANSI C, there's nothing to stop you #define'ing NULL yourself. I was in the habit of using explicit casts, (char*)0 and the like. > Redemption from this sin is on its way. Modern > compilers define "NULL" as "(void *) 0" No. ANSI-compliant compilers have *permission* to define NULL this way, but all three of 0, 0L, and (void*)0 are allowed. _Some_ compilers will define NULL to be (void*)0, but other compiler writers will have compassion on the idiots who wrote "char x = NULL;" and not break their programs. [3. static] This could be argued several ways. I have long had in my personal header file #define public #define private static "static" is not such a big deal. Remember, the norm for good C style is to have a very small number of functions in a file, which means that 'static' functions are expected to be rare. If you knew how BCPL used to tie things together, you would agree that the way C does it is a _big_ improvement. (Would you believe an explicit "global vector" with externals identified by number?) [4. break] There are two separate and distinct issues here, and it really doesn't help to confuse them. 1. "case xxx:" in C is just a label, and no more contains a jump than any other kind of label. So you can "fall through" into the next case. 2. The way you say "get out of the switch () statement" is the same as the way you say "get out of a while ()", so it is pointlessly hard to have a switch case that exits a loop. BCPL had property 1, but not property 2. In BCPL, the way that you said "get out of the switch ()" was ENDCASE, while the way that you said "get out of the loop" was BREAK. It is a mystery to me why B or C ever changed this. But it is important to be clear about the fact that the two properties are quite independent. [5. function definitions] > The Fifth Original Sin was the way functions are defined. > The entire parameter list has to be written twice. This meant that one could easily _add_ parameter type information to existing V5 or V6 code. It also imitated Algol 60 and Fortran. It wasn't by any means an innovation. I actually rather like the Algol/Fortran/classic-C style of function header, because it is very easy to indent consistently (unlike the Algol 68/Pascal/Ada approach), and is a splendid opportunity to place the usual explanatory comment: type foo(x, y, z) xtype x; /* say what x is all about */ ytype y; /* say what y is all about */ ztype z; /* say what z is all about */ > Most programmers have written something like > "strcmp(s,t)", forgetting the declaration "char > *s,*t;". What you wind up with in most cases is, not a > function that fails, but something worse--a function > that works as long as pointers and integers are the > same size, and then fails when you try to port it. I don't understand this. If the body of the function refers to *s or *t (and it is hard to imagine an implementation of strcmp that didn't) the compiler will catch it. Nothing about the relative sizes of pointers and integers is involved. Other parts of the program (in classic C) don't know the argument types _anyway_, so we're only concerned here with the effect on the function definition. I can imagine problems if int and long aren't the same size, but how do you get something where pointer/int is a likely confusion? [7. eight-character names] > The Seventh Original Sin was the eight-character limit > on distinguishable names, or even fewer than eight for > externally defined names. Of course, some such > limitation was required for efficient implementation, ... > for English about 20 [characters] should be sufficient. This simply isn't true. No limitation whatsoever is necessary for efficient implementation. 20 is nowhere *near* enough for English, and 31 is barely adequate. Recall that from quite early days, C has been used on other platforms than UNIX. Name length restrictions are a regrettable fact of life with other people's linkers. Blame the linkers, not the language. I can't say that I was impressed by the UNIX V7 linker's restrictions, but do bear in mind that it had to work in an address space of 64kb *total*. It was a big improvement on the IBM 1130's 5-character limit! The ANSI C standard imposes *no* limit on the number of characters that a C system *may* consider significant. A compiler _may_ look at only the first 31 characters, but it _may_ regard them all as significant. A portable program must ensure that external identifiers are unique in the first six characters (after case folding, too), but that's not a sin in C, it's a fact of life about other people's linkers. On a modern UNIX system, all the characters are significant. > But we must abandon the false god of 100% upward compatibility. C is a language with a history. To adapt a metaphor from S.J.Gould, think of it as a panda, but as a panda that's all thumbs (:-). There never has been a goal (let alone a god) of 100% upward compatibility. But the goal of 95% upwards compatibility was vital: if the standard had been too radically different, it wouldn't have been usable as a standard for *C*. -- Fixed in the next release.
mcdaniel@adi.com (Tim McDaniel) (09/26/90)
In an otherwise excellent article, ok@goanna.cs.rmit.oz.au (Richard A.
O'Keefe) writes:
[File-scope] "static" is not such a big deal. Remember, the norm
for good C style is to have a very small number of functions in a
file, which means that 'static' functions are expected to be rare.
Like the old punchline, "what you mean 'WE', paleface?"
Here at Applied Dynamics (plug plug), and doubtless at many other
places, we try to modularize. All the functions that relate to one
data structure or data type are put in one file, and any local
information is declared 'static' (actually, PRIVATE). If that makes a
large source file, well, life is full of little tragedies.
I dislike the one function==one file idea, simply because so much is
made global. I can't easily tell where an external variable or
function is used.
(P. S. It's a Lone Ranger joke.
"Tonto! Look! Ten thousand Indian warriors have surrounded us,
and they're about to charge! We're about to be killed!"
)
--
Tim McDaniel Applied Dynamics Int'l.; Ann Arbor, Michigan, USA
Work phone: +313 973 1300 Home phone: +313 677 4386
Internet: mcdaniel@adi.com UUCP: {uunet,sharkey}!amara!mcdaniel
johnb@srchtec.UUCP (John Baldwin) (09/28/90)
In article <3835@goanna.cs.rmit.oz.au> ok@goanna.cs.rmit.oz.au (Richard A. O'Keefe) writes: > > while on an IBM/360 signed char was much more costly >than signed char. Indeed, I CAN believe that is the case on a 360! :-) :-) :-) :-) [typos just don't get much better than this! jtb- sorry for the clutter; I couldn't resist.] -- John T. Baldwin | "Pereant qui ante nos nostra dixerunt!" Search Technology, Inc. | (A plague on those who said our good johnb%srchtec.uucp@mathcs.emory.edu | things before we did!)