[comp.lang.c] enum

daveb@laidbak.UUCP (10/02/87)

Trick or Trap?

A program I'm writing uses several ENUMs for variables and function
return types. I have need to enforce a certain ordering of data flow,
so I was comparing ENUM to ENUM as in the program below:

main()
{
	enum { A, B, C } varA, varB;

	printf("A     < B    is %c\n", A <  B ? 'T' : 'F');
	printf("A     > B    is %c\n", A >  B ? 'T' : 'F');
	printf("C    == C    is %c\n", B == B ? 'T' : 'F');

	varA = A; printf("varA = A (%d)\n", varA);
	varB = B; printf("varB = B (%d)\n", varB);

	printf("varA  < varB is %c\n", varA  < varB ? 'T' : 'F');
	printf("varA  > varB is %c\n", varA  > varB ? 'T' : 'F');
	printf("varA == varA is %c\n", varA == varA ? 'T' : 'F');

	exit(0);
}

Both lint and cc complain:

"enum.c", line 12: illegal comparison of enums
"enum.c", line 13: illegal comparison of enums

I can shut them up and make the program work by casting varA and varB
to int for the < and > operations. (i.e. (int)varA < (int)varB ).

According to Harbison & Steele, "C: A Reference Book 2/e", section 5.5:

	"In addition to assigning values of enumeration types, the
	 programmer can test two values for equality.

	 Enumeration types are implemented by associating integer values
	 with the enumeration constants, so that the assignment and
	 comparison of values of enumeration types can be implemented
	 as integer assignment and comparison.
	 ...
	 2. The first enumeration constant receives the value 0 if no
	    explicit value is specified.
	 3. Subsequent enumeration constants without explicit associations
	    receive an integer value one greater that the value associated
	    with the previous enumeration constant.
	 ...
	 As a matter of style, we suggest that programmers treat enumeration
	 types as different from integers and not mix them in integer
	 expressions without using casts."

Questions:
	Is my solution portable? (I think so, but I can't test it.)
	Why does the constant comparison work but not the original variable
		comparison?
	

As an aside:
	If you need an authoritative reference on the C language, the
	book by Harbison & Steele is an *excellent* investment, and is
	'available in finer bookstores everywhere'.
-- 
--------------------"Well, it looked good when I wrote it"---------------------
 Verbal: Dave Burton                        Net: ...!ihnp4!laidbak!daveb
 V-MAIL: (312) 505-9100 x325            USSnail: 1901 N. Naper Blvd.
#include <disclaimer.h>                          Naperville, IL  60540

gwyn@brl-smoke.UUCP (10/04/87)

In article <1177@laidbak.UUCP> daveb@laidbak.UUCP (Dave Burton) writes:
>	enum { A, B, C } varA, varB;
>	printf("varA  < varB is %c\n", varA  < varB ? 'T' : 'F');
>"enum.c", line 12: illegal comparison of enums

For a while, some C compilers attempted to make enums act like a "set"
type, and in the absence of ordering (PRED, SUCC operators), inequality
comparison was in general ill-defined.

ANSI C intends to have enums act much more like integers, the way older
C compilers had them, so (I think) the inequality test would work.
This may seem a step backward, but there is existing code that depends
on int-like behavior from enums.

karl@haddock.ISC.COM (Karl Heuer) (10/05/87)

In article <1177@laidbak.UUCP> daveb@laidbak.UUCP (Dave Burton) writes:
>[Attempt to use relational operators on enums produces warning]

I just had the same thing happen to me.  I was somewhat surprised that lint
complained, because I've always considered that to be a valid operation even
in the strict-enum model.  My workaround was to write a macro
  #define enum_foo_lt(x,y) ((int)(x) < (int)y)
, but I dislike this because it doesn't typecheck the arguments (it will now
let me compare an enum foo with an int, which *should* be illegal in this
model).

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

rbutterworth@orchid.UUCP (10/06/87)

In article <1314@haddock.ISC.COM>, karl@haddock.ISC.COM (Karl Heuer) writes:
> I just had the same thing happen to me.  I was somewhat surprised that lint
> complained, because I've always considered that to be a valid operation even
> in the strict-enum model.  My workaround was to write a macro
>   #define enum_foo_lt(x,y) ((int)(x) < (int)y)
> , but I dislike this because it doesn't typecheck the arguments

If the program is all in one source file:

    #if defined(lint)
        int enum_foo_lt(x,y) enum foo x,y; { return (int)x < (int)y; }
    #else
    #   define enum_foo_lt(x,y) ((int)(x) < (int)y)
    #endif

If it is in multiple files, put the function definition in a file
by itself without any of the #if stuff, and in "foo.h" put

    extern int enum_foo_lt();
    #if !defined(lint)
    #   define enum_foo_lt(x,y) ((int)(x) < (int)y)
    #endif

In either case, lint will see the function and give strict type
checking and the compiler will see the macro and give the speed.

Note that the second case matches nicely with the way ANSI is
setting up the standard library.  You include "foo.h" and get
access to enum_foo_lt() without knowing whether it is a macro,
a real function, or even a compiler built-in.  If you want to
use it as a real function (e.g. take its address) you must first
"#undef enum_foo_lt" and then "func(&enum_foo_lt);".
(too bad there isn't a #pushdef and a #popdef :-)

karl@haddock.ISC.COM (Karl Heuer) (10/08/87)

In article <11053@orchid.waterloo.edu> rbutterworth@orchid.waterloo.edu (Ray Butterworth) writes:
>    extern int enum_foo_lt();
>    #if !defined(lint)
>    #   define enum_foo_lt(x,y) ((int)(x) < (int)y)
>    #endif

Yes, I thought about doing something like this.  I'll probably do that.  (I've
been hesitating only because the program in its current form passes lint -- no
warnings at all, in 1306 lines -- without using the universal "#ifdef lint"
escape and with no (void) casts except on strcpy/strcat.  It's a work of art!)

>Note that [this] matches nicely with the way ANSI is setting up the standard
>library.  You include "foo.h" and get access to enum_foo_lt() without knowing
>whether it is a macro, a real function, or even a compiler built-in.  If you
>want to use it as a real function (e.g. take its address) you must first
>"#undef enum_foo_lt" and then "func(&enum_foo_lt);".  (too bad there isn't a
>#pushdef and a #popdef :-)

That isn't necessary.  "func(&enum_foo_lt)" will get you the real function
even without the "#undef", since function-like macros in ANSI C are only
recognized when the next token is a left parenthesis.

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint