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