[net.lang.c] Enums

dmr (11/08/82)

There has been a lot of grousing about the uselessness of the enum type
in C, most of it justified under the circumstances.  The circumstances
are that all versions of PCC that I know of are buggy in their treatment
of this type.

Enums were intended to be entirely equivalent to ints; just a way, really,
of defining names for constants understood by the compiler and subject
to the normal scope rules.

There was a clear choice here: enums as utterly separate collections of atoms,
more or less as in Pascal, or as ways of naming integers.  I chose the
latter after some waffling.  Unfortunately, some of the waffle batter
got mixed in with PCC and has stayed there.

		Dennis Ritchie

jeff@rlgvax.UUCP (07/05/83)

As understand it, the enum type in C is a adaptation of one of Pascal's
nicer features.  Its motivation is not to enable types to be packed
tighter than in ints, and definitely not to allow tricky combinations
of enums and ints in expressions.

Enums deliberately allow (or should allow) less flexibility than ints.
The idea here is the general idea behind strong typing--have the person
declare what something is, rather than how it is to be implemented, and
disallow all expressions which involve disparate types which happen to
have the same implementation.

The things enums should replace were (and almost always still are)
implemented as #define's.  The signals (from SIGNAL(2) and signal.h)
and the system call error numbers (INTRO(2) and error.h) are two prime
examples of things begging to be made enums.  Anyone trying arithmetic
with them, or comparing them to ints, longs or chars, signed or
unsigned, or for that matter, comparing them with others of the same
type except for == and !=, is probably making a mistake and deserves to
have a type cast forced on him.  The object of the game with enums is
to allow you less flexibility!  If you are going to complain that you
cannot add them to a pointer, you do not really want enums, and
probably either do not understand or believe in, or both, their
motivation, which is somewhat alien to C.

So after all this, you ask, do I use enums.  No, I don't.  I have to
port my code and many C compiler writers feel they need only obey the
K&R book, and do not implement them well.  For example, one compiler I
have heard of disallows enums in switch statements, making them next to
useless.  They come from the same set of identifiers as ordinary
identifiers, according to the "Recent Changes" document, which is sort
of like not allowing two different ints to take the value 5.  Enum
constants, like int constants, are values--just not numeric.  I may get
up the courage to try them again someday.

               Jeffrey Kegler
               CCI Office Systems Division (formerly RLG)
               ...{allegra,seismo,mcnc,lime,we13,brl-bmd}!rlgvax!jeff

guy@rlgvax.UUCP (07/05/83)

The compiler you are referring to did not deliberately refuse to allow "switch"
on an enum; it was a compiler bug which has since been fixed.  The original
VAX-11 PCC allowed "switch" on enums, and any port of the PCC to a new machine
should preserve that characteristic; this one fell through the cracks. The
fact that the names for the enums come out of a global pool is a more serious
problem; it was also a problem with structure members until the language was
changed for the System III compiler (which also comes with 4.?BSD).  The same
change is a little more complex for enums; with structure members, you can
always disambiguate a member reference by looking at the type of the structure
being looked at, as the member is either being used as "foo.memb" or
"foop->memb" (and the compiler is somewhat insistent about NOT attaching a
member name to anything other than a structure of the proper type or a pointer
to such a structure).  Enum member names, however, sit by themselves as plain
constants, and the only available context is the other terms of the expression.
You would have to insist on something like:

enum color { red, white, blue };
enum moon { new, quarter, half, full, harvest, blue };

enum color hue;
enum moon luna;

if (luna == moon.blue)
	...

which is what somebody said in an earlier article was what ADA did.  This
would require a somewhat incompatible extension to the language, although
one could elide the enum name if it was unambiguous ("white" would be
"color.white" as there is no "moon.white").

	Guy Harris
	{seismo,mcnc,we13,brl-bmd,allegra}!rlgvax!guy

mark@cbosgd.UUCP (07/06/83)

The real reason people don't use enums in C much is that PCC is
too restrictive with them.  The main problem is that you can't ++
or -- them, making for loops iterating over them impossible.
You also can't do < or > comparisons on enums.
For example, if SIG* were made an enum, there would be no way
to catch all signals.  If E* (errnos) were made enums, there
would be no way to check errno for a legal value.  There is
no way to have an array whose subscript is an enum, making it
impossible to have an array of strings for perror, or an array
of old values for signals.

smb@ulysses.UUCP (07/06/83)

	From: mark@cbosgd.UUCP
	Newsgroups: net.lang.c
	Subject: Re: enums
	Message-ID: <108@cbosgd.UUCP>
	Date: Tue, 5-Jul-83 23:08:08 EDT
	References: <754@rlgvax.UUCP>, <755@rlgvax.UUCP>

	The real reason people don't use enums in C much is that PCC is
	too restrictive with them.  The main problem is that you can't ++
	or -- them, making for loops iterating over them impossible.
	You also can't do < or > comparisons on enums.
	For example, if SIG* were made an enum, there would be no way
	to catch all signals.  If E* (errnos) were made enums, there
	would be no way to check errno for a legal value.  There is
	no way to have an array whose subscript is an enum, making it
	impossible to have an array of strings for perror, or an array
	of old values for signals.

Note that Pascal's enumerations suffer from none of these problems.
Enumerations in Pascal are by definition an ordered set, with the
operations 'pred' and 'succ' permitted (though you can't do 'pred' on
the first element, nor 'succ' on the last).

jonab@sdcrdcf.UUCP (07/06/83)

enums would be much better if you were allowed to use the "++" and
"--" operators on them to implement the pascal pred and succ functions.
Also, you cannot use them in array subscripts.
I would like to be able to write:

enum color {red, white, blue};
enum color hue;
int palate[color];

	hue = red;
	do {
	    if (palate[hue]) {
		/* some code */
	    }
	    if (hue != blue)
		hue++;
	} while (hue != blue);

As they stand, they are not overly useful without a lot of ugly type
conversion.

jmc@root44.UUCP (07/07/83)

I think enums are a nice concept, as I always believe in making the computer
do the work for me, and thinking of numbers is something it can do better.

Where they do break down in C (in my view) is that there is no syntax for
initialising an array with enum indices.  Thus you cannot simply replace 
error.h numbers for example, and still have sys_errlist[] defined in the
same way.  Most of my attempts to use enums have foundered on this particular
rock and I have ended up with defines, not wanting to write lots of code
for what the C compiler ought to be doing.

		John Collins
		Root Computers Ltd
			...!vax135!ukc!root44!jmc

cg@myrias.UUCP (Chris Gray) (09/30/85)

Actually, it's not hard at all to come up with a model for enum's that is
both efficient and useful. I have such a model implemented in a compiler I
wrote (not a C compiler). Type 'bool' is also built-in, so I don't have any
need to allow an enum type to be used directly in 'if's, 'while's, etc.

First, don't allow the programmer to specify specific values for the enum
constants. This, in my opinion, violates the whole point of enumerations
(a list of specific values which are the only values allowed - there is no
implication about what the values look like). C's allowing two names to
have the same value seems very dangerous. Usefulness is added, and not too
much danger, by adding ordering to the enumeration type, such that later
names in the list are greater than previous ones. By defining that each is
exactly 1 greater than the previous, we can allow the following:

    ordering is clearly defined for all legal values
    adding/subtracting an integer simply yields another enumeration value
        which is correspondingly further along or back in the list of names.
	(we politely ignore out-of-range values here just as we do for
	integer overflows)
    converting to integers is done by subtracting the first value, and
        converting from integer is done by adding the first value. e.g.
	
	type FRUIT = enum {APPLE, PEAR, ORANGE, BANANA};
	type DIRECTION = enum {NORTH, EAST, SOUTH, WEST};
	FRUIT f1, f2;
	int i1, i2;
	DIRECTION d;
	...
	f1 := i1 + APPLE;	/* f1 is the i1'th FRUIT */
	i2 := f2 - APPLE;	/* i1 is the index of f2 */
	d := (d - NORTH + 1) % 4 + NORTH;	/* turn clockwise */
	d := (d - NORTH + 5) % 4 + NORTH;	/* turn counter-clockwise */

We don't even have to specify that the first value is represented as 0, but
that would seem to be the most efficient. As an experiment, I treated type
'char' as an enumeration with a funny syntax for it's constants. It has
worked out quite well.

			Chris Gray   ..alberta!myrias!cg

barmar@mit-eddie.UUCP (Barry Margolin) (10/07/85)

Allowing the programmer to specify the actual values for enums is a very
important feature in systems programming.  In many areas, one is handed
a protocol or device specification and one must write a driver.  The
specs usually contain arbitrary sets of values for some fields.  It is
very attractive to be able to use enums for these fields.  #defined
constants could be used, as they would have been before enums were added
to the language, but enums were invented to reduce the need for such
untyped constants.
-- 
    Barry Margolin
    ARPA: barmar@MIT-Multics
    UUCP: ..!genrad!mit-eddie!barmar