[comp.lang.c] Definition of boolean type

awd@dbase.UUCP (Alastair Dallas) (02/04/89)

We've discussed the proper definition of NULL a great deal, and I understand
that the ANSI standard now specifies it.  But what about a boolean type?
Please excuse me if the standard discusses this as well, but I can think
of several ways to handle booleans (and I've worked on code that used all
three):

1.	typedef short bool;

2.	typedef char bool;

3.	typedef enum {FALSE=0, TRUE=1} bool;

I like the last one, but it requires a compiler that doesn't complain about
constructs like:

	bool flag = TRUE;

	if (flag)
		...

Some compilers I've run into want that to be either:

	if ((short) flag)

or

	if (flag == TRUE)

While this means more typing*, I'm inclined to forgo the convenience of
if (flag) in favor of the self-documentation of the explicit if (flag==TRUE).

I'm curious what the net thinks.  Are there any other problems with using
an enum?  While we're at it, I'd like to hear opinions pro and con on the
use of all upper case to identify typedefs--bool vs. BOOL vs. Bool.

/alastair/

*No pun intended.

gwyn@smoke.BRL.MIL (Doug Gwyn ) (02/08/89)

In article <10@dbase.UUCP> awd@dbase.UUCP (Alastair Dallas) writes:
>But what about a boolean type?
>Please excuse me if the standard discusses this as well, but I can think
>of several ways to handle booleans (and I've worked on code that used all
>three):
>1.	typedef short bool;
>2.	typedef char bool;
>3.	typedef enum {FALSE=0, TRUE=1} bool;
>I like the last one, but it requires a compiler that doesn't complain about
>constructs like:
>	bool flag = TRUE;
>	if (flag)

ANSI C compilers have to accept that.  Enums act just like ints in
expressions.  Some C compilers tried to make more full-fledged types
out of enumerations, with mixed success (generally unsatisfactory).
One occasionally still runs into such compilers, as you've noticed.

>While this means more typing*, I'm inclined to forgo the convenience of
>if (flag) in favor of the self-documentation of the explicit if (flag==TRUE).

If your Boolean variable is called `flag', that may be more intelligible,
but what about, for example,
	if ( too_many )
which is more readable the other way.  Generally I don't think of ==
or != as Boolean operators.

>I'm curious what the net thinks.

I don't know about the net; does it think?  My typedef for `bool' is
as an int, because that is the actual type of C Boolean expressions.

>use of all upper case to identify typedefs--bool vs. BOOL vs. Bool.

As a matter of style, if something is considered a (local) standard
extension to the base language, as with my <std.h> use of `bool' and
`pointer' (generic pointer type), then it should look like other
keywords.  Something that looks like a function should look like a
function (locally that means `TooMany()' etc.).  Manifest constants
generally should follow existing practice and appear as all-caps.
However, the most important thing is that your code be maintainable;
follow whatever stylistic guidelines best enforce that.

evil@arcturus.UUCP (Wade Guthrie) (02/10/89)

In article <9609@smoke.BRL.MIL>, gwyn@smoke.BRL.MIL (Doug Gwyn ) writes:
> In article <10@dbase.UUCP> awd@dbase.UUCP (Alastair Dallas) writes:
> >But what about a boolean type?

ack phht.  Many people have been programming in C for years and have
gotten quite used to C's handling of boolean (translate: int) stuff 
(I even like it).  More importantly, any change to C to include a
boolean type would make a lot of existing code break.  

> >of several ways to handle booleans (and I've worked on code that used all
> >three):
> >1.	typedef short bool;
> >2.	typedef char bool;
> >3.	typedef enum {FALSE=0, TRUE=1} bool;

Why make a simple idea more complicated?  The only place in the code where
this will make any difference is in the declarations (with, possibly, the
exception of the enum form).  The same thing can be done with commenting:

	int	blah,	/* this variable . . . */
		fred,
		flag,	/* boolean: set if ... */
		. . .
 
> >if (flag) in favor of the self-documentation of the explicit if (flag==TRUE).

You can do that whether the type is 'int' or 'bool'.  That is a matter of
style and is certainly called for in many cases.

> 	if ( too_many )

I usually try to make things look like that -- I find it takes a little
more effort in the variable names, but it makes the code a lot more readable.

#ifdef DIGRESSION_OKAY

	Now, if you want to know about putting effort into making 
	variables understandable. . .You got a graphical object which 
	is made up of a linked list of pieces (such as lines, square 
	panels, etc).  You need a structure for the linked list node 
	and a separate structure for each type of piece.  With this 
	arrangement, you could implement a union of pointers to 
	structures (each member of this union points to a different 
	type of piece).  How about this for readable variable names:

	struct ll_node
	{
		struct ll_node *next_piece;
		union
		{
			struct s_panel	*panel;
			struct s_line	*line;
			. . .
		} is_a;
	} object;

	So that in the code one might see:

	something = object.next_piece->is_a.panel

#endif /* DIGRESSION_OKAY */

> generally I dont think of == or != as Boolean operators.

Me either.
 

Wade Guthrie
evil@arcturus.UUCP
Rockwell International
Anaheim, CA

(Rockwell doesn't necessarily believe / stand by what I'm saying; how could
they when *I* don't even know what I'm talking about???)

msb@sq.uucp (Mark Brader) (02/10/89)

> 1.	typedef short bool;
> 2.	typedef char bool;
> 3.	typedef enum {FALSE=0, TRUE=1} bool;

The last is nicely self-documenting, as long as you understand that the
language will never enforce it for you, but as you noted, some existing
compilers have problems with it.  (In the ANSI draft, all the constructs
you want do work.)

However, for ordinary use I prefer none of these.  My booleans are int
when they are scalars or in small arrays: I let the computer work in its
favorite size.  When the arrays become large enough to make space conser-
vation important, then of course I switch to char -- unless the array is so
big that I have to use shifts and masking to pack a boolean into each bit.

I prefer not to use typedefs for boolean for two reasons.  One is that I
couldn't make that decision to change types according to the array size
(unless I had two typedefs, of course).  The other reason is that I prefer
to use typedefs sparingly, because they conceal information that's
sometimes useful.  (I know that variable has a numerical value, but do I
need %d format to print it, or %ld or %u or %lu or %g...?)

Basically I reserve typedefs for occasions when they will significantly
enhance either brevity or modularity.  Typedefing boolean does neither,
but only adds a bit (no pun intended) of documentation.  This can be done
just as well with a comment beside the declaration, or better yet, by
choosing a suitable name for the variable itself.

Thus, instead of this...		I prefer this...

boolean sex;				int is_female;	/* boolean */
struct country {			struct country {
	char name[CNAMELEN+1];			char name[CNAMELEN+1];
	boolean driving;			char drives_on_right;
							/* boolean */
} country[200];				} country[200];


The worst thing a person who defines TRUE and FALSE can do, though, is
"if (x == TRUE)".  This is a true abomination.  If the variable is true to
its logical type, it is true that this is equivalent to "if (x)", but it's
also true that it's very likely to be slower.  And if the variable is
*not* true to its logical type, i.e. if it may sometimes have a value
other than 0 or 1, then the code becomes extremely tricky for the logical
programmer to debug!

(All puns intended.  For the benefit of non-users of FORTRAN: "logical"
 is the name of the boolean type in that language.)

On the other hand, "is_female = TRUE;" is reasonable.  Some days I even
prefer it to the alternatives.


> use of all upper case to identify typedefs--bool vs. BOOL vs. Bool.

I figure typedefs are almost like macros, so I use BOOL.  Opinions vary.

Mark Brader, SoftQuad Inc., Toronto, utzoo!sq!msb, msb@sq.com
#define	MSB(type)	(~(((unsigned type)-1)>>1))

karl@haddock.ima.isc.com (Karl Heuer) (02/11/89)

In article <3645@arcturus> evil@arcturus.UUCP (Wade Guthrie) writes:
>>In article <10@dbase.UUCP> awd@dbase.UUCP (Alastair Dallas) writes:
>>>But what about a boolean type?
>
>ack phht.  Many people have been programming in C for years and have
>gotten quite used to C's handling of boolean (translate: int) stuff 
>(I even like it).

The same could have been said of `void'% before it was added to C.  Just
because it's possible to do without it doesn't make it a bad idea.

>More importantly, any change to C to include a boolean type would make a lot
>of existing code break.

Nonsense.  I can easily come up with a model that is useful, yet completely
backward compatible with existing code (modulo the new keyword, and even that
can be avoided).

>The same thing can be done with commenting:
>	int	flag,	/* boolean: set if ... */

Adding it to the language would make the information available to the compiler
as well as the reader.  This would allow more errors to be caught at compile
time, and it could sometimes produce better code.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint
________
% I'm sure there are some Neanderthals out there that still eschew `void'.
  You need not reply just to state your existence.

chris@mimsy.UUCP (Chris Torek) (02/11/89)

In article <3645@arcturus> evil@arcturus.UUCP (Wade Guthrie) writes:
>Why make a simple idea more complicated?

Indeed.

Perhaps the biggest problem with boolean types, and definitions for
`TRUE' and `FALSE' (or True and False or true and false or . . .)
is exemplified by the QDSS driver supplied with 4.3BSD-tahoe (for
which no one at Berkeley is at fault).  It had the following definitions
(somewhat paraphrased):

	#define FALSE 0
	#define TRUE ~FALSE
	...
	/* these macros return TRUE when the queue is empty/full/... */
	#define ISEMPTY(eq) ((eq)->head.foo == (eq)->tail.foo)

The code then read

		if (ESEMPTY(eq) == TRUE) bar();

It is left as an exercise to the reader to determine why this test
will never call function bar().
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

mcdonald@uxe.cso.uiuc.edu (02/11/89)

>	#define FALSE 0
>	#define TRUE ~FALSE
	...
>	/* these macros return TRUE when the queue is empty/full/... */
>	#define ISEMPTY(eq) ((eq)->head.foo == (eq)->tail.foo)

>The code then read

>		if (ESEMPTY(eq) == TRUE) bar();

>It is left as an exercise to the reader to determine why this test
>will never call function bar().
And on some computers
      (FALSE == TRUE)   returns 0, while on others
      (FALSE == TRUE)   will return 1! (Hint: There are a few, a very
few, C compilers for one's complement machines, and on some of those
0 == -0). I admit this is farfetched.

jas@ernie.Berkeley.EDU (Jim Shankland) (02/12/89)

In article <15906@mimsy.UUCP> chris@mimsy.UUCP (Chris Torek) writes:
>Perhaps the biggest problem with boolean types, and definitions for
>`TRUE' and `FALSE' ... is exemplified by ...:
>
>	#define FALSE 0
>	#define TRUE ~FALSE
>	...
>	/* these macros return TRUE when the queue is empty/full/... */
>	#define ISEMPTY(eq) ((eq)->head.foo == (eq)->tail.foo)
>
>The code then read
>
>		if (ESEMPTY(eq) == TRUE) bar();

Bleck.  But this is no more an argument against boolean types than an
incompetent surgeon is an argument against surgery.  First, TRUE ought
to be defined to be the same value returned by relops:  1.  Second,
as Mark Brader has pointed out, "if (<bool-val> == TRUE)" is an abomination.
Do you say, "If the weather's nice tomorrow is true, I'll have a picnic"?
Or "If the queue is empty is true, I'd better call bar()"?

I agree with Karl Heuer:  C would have benefited from a built-in boolean
type.  As is, conscientious programmers will avoid type punning between
values that are conceptually boolean with those that are conceptually
integers (though as far as C is concerned, of course they're both integers.)

Jim Shankland
jas@ernie.berkeley.edu

"I've been walking in a river all my life, and now my feet are wet"

les@chinet.chi.il.us (Leslie Mikesell) (02/13/89)

In article <27989@ucbvax.BERKELEY.EDU> jas@ernie.Berkeley.EDU.UUCP (Jim Shankland) writes:

>I agree with Karl Heuer:  C would have benefited from a built-in boolean
>type.

Or perhaps a boolean equality operator for people who don't like the looks
of:
(1)    if (expression) action();
and prefer:
(2)    if (expression == SOMETHING) action();
and who don't like:
(3)    if (expression != 0) action();

Method (2) would work (and look better to some people) if C had a boolean
equality operator that would evaluate false only when one of the operands
is zero and one non-zero.  This would have an advantage over a separate
data type in that you would not have to convert the return values from
all those functions that return 0 for failure, some (useful, non-boolean)
value on success.

Les Mikesell

bph@buengc.BU.EDU (Blair P. Houghton) (02/13/89)

In article <27989@ucbvax.BERKELEY.EDU> jas@ernie.Berkeley.EDU.UUCP (Jim Shankland) writes:
>
>I agree with Karl Heuer:  C would have benefited from a built-in boolean
>type.  As is, conscientious programmers will avoid type punning between
>values that are conceptually boolean with those that are conceptually
>integers (though as far as C is concerned, of course they're both integers.)

Low-sophistication point-of-interest, approx. 20 mills in value:

You can be conscientious if, when unsure of the return value, and you _like_
having true==1 and false==0, then say 

	boolfoo = !!expr_returning_funny_bools;

which, according to my KnR, should never ever give any boolfoo other
than 0 or 1, even if the expr has a dozen different values for true.

(See the description of the '!' op.  I think it's around page 187 of ed. 1.)

				--Blair
				  "!!(But I can't be sure...)"

bill@twwells.uucp (T. William Wells) (02/13/89)

In article <2113@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes:
:       boolfoo = !!expr_returning_funny_bools;
:
: which, according to my KnR, should never ever give any boolfoo other
: than 0 or 1, even if the expr has a dozen different values for true.

It'll work, but try

	boolfoo = expr_returning_funny_bools != 0;

It's clearer.

---
Bill
{ uunet!proxftl | novavax } !twwells!bill

evil@arcturus.UUCP (Wade Guthrie) (02/14/89)

Attempting to fill the part of those called 'reader':

In article <15906@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:
 
> 	#define FALSE 0
> 	#define TRUE ~FALSE
		/* TRUE has the bit pattern 1111 ... 1111, not 0000 ... 0001 */
> 	...
> 	/* these macros return TRUE when the queue is empty/full/... */
> 	#define ISEMPTY(eq) ((eq)->head.foo == (eq)->tail.foo)

	/* causing the macro ISEMPTY to evaluate to 1 or 0 */
 
> The code then read
> 
> 		if (ESEMPTY(eq) == TRUE) bar();
		/* 
		 * since 1111 ... 1111 never equals either 1 or 0,
		 * bar() never gets called
		 */
 
> It is left as an exercise to the reader to determine why this test
> will never call function bar().

Do I pass the test?


Wade Guthrie
evil@arcturus.UUCP
Rockwell International
Anaheim, CA

(Rockwell doesn't necessarily believe / stand by what I'm saying; how could
they when *I* don't even know what I'm talking about???)

diamond@csl.sony.JUNET (Norman Diamond) (02/14/89)

In article <15906@mimsy.UUCP>, chris@mimsy.UUCP (Chris Torek) writes:

> Perhaps the biggest problem with boolean types, and definitions for
> `TRUE' and `FALSE' (or True and False or true and false or . . .)

> 	#define FALSE 0
> 	#define TRUE ~FALSE
> 	...
> 	/* these macros return TRUE when the queue is empty/full/... */
> 	#define ISEMPTY(eq) ((eq)->head.foo == (eq)->tail.foo)
> 	...
> 	if (ESEMPTY(eq) == TRUE) bar();

> It is left as an exercise to the reader to determine why this test
> will never call function bar().

A boolean operator would make it work correctly:

	#define TRUE !FALSE

Of course, TRUE and FALSE *should* be used only as the right-hand-sides
of assignments, not as operands for a comparison.  However, it is not
correct to say that the biggest problem with <x> is that some programmer
codes a bug in which he forgets to use <x>'s operators.

scs@adam.pika.mit.edu (Steve Summit) (02/19/89)

In article <7698@chinet.chi.il.us> les@chinet.chi.il.us (Leslie Mikesell) writes:
>Or perhaps a boolean equality operator for people who don't like the looks
>of:
>(1)    if (expression) action();
>and prefer:
>(2)    if (expression == SOMETHING) action();
>Method (2) would work (and look better to some people) if C had a boolean
>equality operator that would evaluate false only when one of the operands
>is zero and one non-zero.

How silly.  The thing to do is educate those "people who don't
like the looks of if(expression)".  Who are they, anyway?
If an expression is of "conceptual type boolean," and especially
if its name reflects it ("inputready()", "break_seen", etc.) then
if(expression) is the most natural way to write it.  If the
expression is not "conceptually boolean," then an explicit
comparison is correct and appropriate.

Comparison operators can be thought of as converting ints to
bools, to be handed to things like "if".  Who needs a new
operator to convert bools to bools?

                                            Steve Summit
                                            scs@adam.pika.mit.edu

rickc@pogo.GPID.TEK.COM (Rick Clements) (02/28/89)

In article <1989Feb10.092449.20875@sq.uucp> msb@sq.com (Mark Brader) writes:
}} 1.	typedef short bool;
}} 2.	typedef char bool;
}} 3.	typedef enum {FALSE=0, TRUE=1} bool;

}The last is nicely self-documenting, as long as you understand that the
}language will never enforce it for you, but as you noted, some existing
}compilers have problems with it.  (In the ANSI draft, all the constructs
}you want do work.)

}However, for ordinary use I prefer none of these.  My booleans are int
}when they are scalars or in small arrays: I let the computer work in its
}favorite size.

I always use chars.  This lets the machine I am working on (an 68HC11)
work in its favorite size.  Everyone assumes an int is the natural size
of the machine, but an int is at least 16 bits.

}Basically I reserve typedefs for occasions when they will significantly
}enhance either brevity or modularity.  Typedefing boolean does neither,
}but only adds a bit (no pun intended) of documentation.  This can be done
}just as well with a comment beside the declaration, or better yet, by
}choosing a suitable name for the variable itself.

I perfer to use typedef's for the extra documentation.  Also, if I work
on code that might be ported, it is easy to change to match the machine.

}The worst thing a person who defines TRUE and FALSE can do, though, is
}"if (x == TRUE)".  This is a true abomination.  If the variable is true to
}its logical type, it is true that this is equivalent to "if (x)", but it's
}also true that it's very likely to be slower.  And if the variable is
}*not* true to its logical type, i.e. if it may sometimes have a value
}other than 0 or 1, then the code becomes extremely tricky for the logical
}programmer to debug!

With the compiler I am currently using, I use "if (x == FALSE)" or
"if (x != FALSE).  This doesn't have an ambiguity because of multiple
true values.  The compiler I am using generates LESS code this way. 
("if (x)" causes it to go to the work of converting it to a 1 or 0
with some less than efficient code.  That is ignoring the fact the
whole process it unneeded.)  PROM space is tight, so this becomes 
important.  It also helps when you want to get an entire sequence on
a logic analyzer trace.
-- 
Rick Clements (RickC@pogo.GPID.TEK.COM)

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/01/89)

In article <6849@pogo.GPID.TEK.COM> rickc@pogo.GPID.TEK.COM (Rick Clements) writes:
>With the compiler I am currently using, I use "if (x == FALSE)" or
>"if (x != FALSE).  ...  The compiler I am using generates LESS code
>this way.  ("if (x)" causes it to go to the work of converting it to
>a 1 or 0 with some less than efficient code.

That's pretty strange -- "if(x)" means no more and no less than
"if x is nonzero", which nearly all instruction sets support directly.
It's easy to imagine a dumb compiler that produces MORE code for
"if(x!=0)" but not one that produces LESS code.

guy@auspex.UUCP (Guy Harris) (03/02/89)

 >With the compiler I am currently using, I use "if (x == FALSE)" or
 >"if (x != FALSE).  This doesn't have an ambiguity because of multiple
 >true values.  The compiler I am using generates LESS code this way. 
 >("if (x)" causes it to go to the work of converting it to a 1 or 0
 >with some less than efficient code.  That is ignoring the fact the
 >whole process it unneeded.)

Yup, it sure is unneeded.  Why is the compiler in question so dumb?

 >PROM space is tight, so this becomes important.

Are there any smart compilers for the chip in question?  If so, you
might want to pick them up, given that PROM space *is* so tight....

jeenglis@nunki.usc.edu (Joe English) (03/02/89)

gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <6849@pogo.GPID.TEK.COM> rickc@pogo.GPID.TEK.COM (Rick Clements) writes:
>>With the compiler I am currently using, I use "if (x == FALSE)" or
>>"if (x != FALSE).  ...  The compiler I am using generates LESS code
>>this way.  ("if (x)" causes it to go to the work of converting it to
>>a 1 or 0 with some less than efficient code.
>
>That's pretty strange -- "if(x)" means no more and no less than
>"if x is nonzero", which nearly all instruction sets support directly.
>It's easy to imagine a dumb compiler that produces MORE code for
>"if(x!=0)" but not one that produces LESS code.

I once had to work with a compiler that was broken in a
similar way: boolean expressions would calculate either a 0
or a 1, then test that result and branch accordingly.  This
generated *three unnecessary instructions* for every
relational expression.  (And the fourth pass of the compiler
was called the "optimizer!"  I don't think it did a very
good job of optimizing...)  

If I recall correctly, it generated the same amount of code
for "if(x)" and "if(x!=0)," but it went something like:

	Compare x and zero; 
	move 1 into AX; 
	if test yielded equal, skip next instruction; 
	move 0 into AX;

Then it would do an "OR AX,AX" and branch accordingly.  
Shocking, but true.

Before you ask (so you can avoid purchasing it :-), the
compiler in question is an old (c. 1984) version of Computer
Innovation's C86, I think somewhere around version 1.2.

--Joe English

  jeenglis@nunki.usc.edu

gwyn@smoke.BRL.MIL (Doug Gwyn ) (03/02/89)

In article <2892@nunki.usc.edu> jeenglis@nunki.usc.edu (Joe English) writes:
-gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
->That's pretty strange -- "if(x)" means no more and no less than
->"if x is nonzero", which nearly all instruction sets support directly.
->It's easy to imagine a dumb compiler that produces MORE code for
->"if(x!=0)" but not one that produces LESS code.
-If I recall correctly, it generated the same amount of code
-for "if(x)" and "if(x!=0)," but it went something like:

I can also believe that a compiler would produce exactly same code
for "if(x)" and "if(x!=0)", even if it's horrible code in both cases.
I still wonder how one could get WORSE code for "if(x)", though.