[net.lang.c] Boolean datatypes

cim2@pyuxv.UUCP (Robert L. Fair) (06/13/86)

At the risk of being flamed, I've been using enums for a proper boolean
datatype for years:

	typedef enum { false=0,true } bool;

	bool	flag=false;

	if(!flag)
		...
This has _lots_ of advantages over the usual #defines - especially with
a decent compiler which will barf on constructs like:

	flag=10;	 /* correct syntax error,10 is neither true nor false */
or
	enum	states {activate,inactive,comatose} state;
	flag=state;	 /* correct syntax error - a  state isn't a boolean */

Many current C compilers now implement enums properly (i.e. each enumeration
is a distinct type) yet they are hardly ever used. Any comments why not ?

Rob Fair
Bell Communications Research
ihnp4!pyuxv!cim2

guy@sun.uucp (Guy Harris) (06/13/86)

> At the risk of being flamed, I've been using enums for a proper boolean
> datatype for years:
> 
> 	typedef enum { false=0,true } bool;
> ...
> This has _lots_ of advantages over the usual #defines - especially with
> a decent compiler which will barf on constructs like:
> 
> 	flag=10;	 /* correct syntax error,10 is neither true nor false */

Well, I doubt the compiler would claim that it was a syntax error; PCC
complains about an "enumeration type clash" (which is a warning, rather than
an error).  This is an advantage of "enum"s, though, even though it's just a
warning.  If you fix some bugs in "lint", it even checks that you aren't
passing an "enum" to a routine expecting an "int" or another "enum", or an
"int" to a routine expecting an "enum".

Also, if you use "enum"s rather than #define constants, a reasonable
symbolic debugger will, when asked to print an "enum" variable's value,
print it by its "enum" element name ("dbx" does, I don't know about the
others).

However, there is one problem with using "enum"s for a boolean type;
"Boolean" expressions in C have type "int", not "enum", so

	bool flag = (foobar == 33);

will cause an "enumeration type clash" error; you have to cast the
expression to type "bool" to silence the compiler.

Note that "enum"s, according to the current C draft, are basically integral
types; this means that assignments of "int"s to "enum"s are legal, although
this doesn't mean the compiler can't produce a warning.

> Many current C compilers now implement enums properly (i.e. each enumeration
> is a distinct type)

What C compilers don't?

> ...yet they are hardly ever used. Any comments why not ?

Habit?
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

chris@umcp-cs.UUCP (06/15/86)

In article <210@pyuxv.UUCP> cim2@pyuxv.UUCP (Robert L. Fair) writes:
>At the risk of being flamed, I've been using enums for a proper boolean
>datatype for years:
>
>	typedef enum { false=0,true } bool;
>
>	bool	flag=false;
>
>... a decent compiler which will barf on constructs like:
>
>	flag=10;	 /* correct syntax error,10 is neither true nor false */
>or
>	enum	states {activate,inactive,comatose} state;
>	flag=state;	 /* correct syntax error - a  state isn't a boolean */

Neither of these is syntactically incorrect; a compiler that gives
a syntax error is printing the wrong message.  Rather, these are
semantically incompatible (though this depends on your semantics).

>Many current C compilers now implement enums properly (i.e. each enumeration
>is a distinct type) yet they are hardly ever used. Any comments why not ?

PCC does about half the job, mostly because it gets stickier as you
go along.  For example, should printf() be able to print enumerations?
If so, should it print them as integers or as strings?  Should scanf()
handle enumerations?  Where is the line between the integer/enumeration
split?

The latest-I-have-seen ANSI X3J11 draft has `enum's defined simply
as aliases for integers, which means one might as well just use
`#define'. . . .
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@mimsy.umd.edu

donn@utah-gr.UUCP (Donn Seeley) (06/16/86)

I was informed by Rob Pike not too long ago that at least at the Labs,
enums are ints: enumeration constants are symbolic integer constants
and enumeration variables are simply ints.  Their compiler doesn't emit
any 'enumeration type clash' warnings and performs proper integer
operations on enums such as shifts (the BSD PCC won't permit shifts of
enum variables).  The proposed draft ANSI C standard doesn't seem to
require that compilers treat enums as anything other than ints.  If
people are writing programs which employ enums strictly as ints, the
utility of enums in type verification seems to be nil and compiler
warnings will just be obnoxious.

The situation with enums reflects the situation with typedefs in an odd
way -- despite appearances, neither construct defines a 'new' type.

As Guy Harris notes, enums are useful as replacements for preprocessor
constants and can serve as a debugging aid for programs like 'dbx'.
Given their limited application it's easy to see why they haven't
caught on.

Donn Seeley    University of Utah CS Dept    donn@utah-cs.arpa
40 46' 6"N 111 50' 34"W    (801) 581-5668    decvax!utah-cs!donn

faustus@ucbcad.BERKELEY.EDU (Wayne A. Christopher) (06/17/86)

> 	typedef enum { false=0,true } bool;
> 
> Many current C compilers now implement enums properly (i.e. each enumeration
> is a distinct type) yet they are hardly ever used. Any comments why not ?

The problems come when you write:

	bool b;
	int a, c;

	b = (a == c);

My compiler gives an "enumeration type clash".  It would seem that you
can't legally convert integers to enum members...

	Wayne

alex@ucla-cs.ARPA (Alex Quilici) (06/17/86)

>The problems come when you write:
>
>	bool b;
>	int a, c;
>
>	b = (a == c);
>
>My compiler gives an "enumeration type clash".  It would seem that you
>can't legally convert integers to enum members...

yes, you can.  c just won't do the conversion for you automatically.
try using a cast:

       b = (bool) (a == c);

i frequently use enums to define constants that indicate various errors.
i find that whenever i have to worry about many errors

       typedef enum {NOPASS, BADRD, ... } ERROR;

is more convenient than 

	#define NOPASS 1
	#define BADRD  2
	   ...

i also find that my programs are more readable because the compiler
forces me to declare all variables or functions that hold or return
an error status to be of type ERROR, rather than simply int.

alex

mike@peregrine.UUCP (Mike Wexler) (06/17/86)

In article <210@pyuxv.UUCP> cim2@pyuxv.UUCP (Robert L. Fair) writes:
>Many current C compilers now implement enums properly (i.e. each enumeration
>is a distinct type) yet they are hardly ever used. Any comments why not ?
There are(at least) two reasons.  First, a lot of people don't know that 
there c compiler has enum types since they have a C compiler thats uses
k&r as the manual and k&r doesn't describe enum types.  Second, they would
limit the portability of the application since enum types are not yet 
universal.  I think that the ANSI standard will help solve both of these
problems since the new standard document describing the language will describe
enum's(they are in the standard, aren't they?) and compiler writers will 
have more incentive to support this feature.  Not being ANSI compatible will
cause a compiler writer to receive many complaints(and probably less sales).


-- 
Mike Wexler
Email address:(trwrb|scgvaxd)!felix!peregrine!mike
Tel Co. address: (714)855-3923
;-) Internet address: ucivax@ucbvax.BERKELY.EDU!ucivax%felix!mike@peregrine :-(

rbutterworth@watmath.UUCP (Ray Butterworth) (06/18/86)

The X3J11 draft says that enums are type (int).  This is probably a
mistake since they should be at least type (size_t) as they are handy
for defining specific index values into arrays.

But even though they are more or less equivalent to #defines as far
as the compiler is concerned, there is no reason that lint can't
treat them specially.  In particular it should allow subscripting,
switching, and if(b) and if(!b) without any complaint, but it should
complain about function parameters or other attempts to implicitly
cast them to a different type.

Incidentally, since they are used the same as #define manifests,
as a matter of style we always make the enum constant have an
all upper-case name (e.g. TRUE) just as we do for all #define
manifest names.  As far as the programmer is concerned they are
used identically so he shouldn't need to know whether they are
enums or manifests.  And if we have to move to a compiler that
doesn't have enums, making them manifests in the header files
is easy.  Similarly we make #define macros all upper-case if
they have side-effects on the arguments, but all lower-case if
they are indistinguishable from functions in usage.

As was pointed out, the biggest problem with an enum Boolean is
in assigning "b=(a==c)" or "return (a==c)".  We use this typedef
frequently, and this particular problem doesn't arise that often.
When it does, it really isn't that difficult to have to say
"b=(Boolean)(a==c)" or "return (Boolean)(a==c)", or to have
#define BOOLEAN(x) ((Boolean)(x))
and then "b=BOOLEAN(a==c)" or "return BOOLEAN(a==c)".

guy@sun.UUCP (06/19/86)

> The proposed draft ANSI C standard doesn't seem to require that compilers
> treat enums as anything other than ints.  If people are writing programs
> which employ enums strictly as ints, the utility of enums in type
> verification seems to be nil and compiler warnings will just be obnoxious.

I wouldn't say their utility was nil.  I don't know how many people
literally use them strictly as ints; it would seem kind of silly to set some
"int" variable to 33 by doing

	enum bag_of_numbers { thirty_three = 33, sixty_six = 66, ... };

	variable = thirty_three;

I suspect that in most cases an "enum" is being used roughly the same way
it's used in other languages, to represent a member of a collection of named
objects.  The difference in C is that you can bind values to these names,
either because the values are imposed by some external agency (error codes
in a protocol or from an external device, etc.) or because one wishes to
implement some function whose domain is the set of values for that "enum"
using some efficient trick involving arithmetic on the underlying value of
the "enum".  As such,

	enum apples { jonathan, ... } apple;
	enum oranges { jaffa, ... } orange;

	if (apple == orange) {
		...
	}

would be, well, comparing apples and oranges, and should get a warning.

You can use casts to override this;

	if (apple == (enum apples) orange)

should not give a warning.

Another reason why "enum"s need to be treated as ints is that you may want
to use them as subscripts.  Other languages handle this by supporting
subrange types and treating an array as a mapping from some integral or
enumerated type to some other type, so that you declare the bounds of an
array by giving the type to be used as an index.  You don't do this in C
(for one thing, C doesn't support subranges, so you can't declare a
10-element array by saying its index is the subrange 1..10; for another
thing, C's notion of array indexing is a bit strange), so you can't say
"this array has "enum apples" as its subscript.

I hope future ANSI C compilers don't treat "enum"s totally like arrays.  For
one thing, they'd better not put "this is just an integral type of some
particular size" in the debugger's symbol table; they should specify that
it's an enumerated type and give the names of the type's values.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)