more why I dont like C enums

utzoo!decvax!ittvax!swatt utzoo!decvax!ittvax!swatt
Sat Nov 6 15:15:20 AEST 1982


I confess I never had the patience to find all the problems with
C enumerated types that Mark Horton detailed.  My use of enums
is limited to:

	typedef enum { NO, YES } BOOL;

Now while this lets me do:

	BOOL flag = YES;

	if (flag) ...

It doesn't let me do:

	BOOL
	iseof () {
		int n = 0;

/*###26 [cc] warning: enumeration type clash, operator RETURN %%%*/
		return (n == 0);
	}


You have to:

		return ((BOOL) n == 0);

However, for such limited use it is at least clearer in the source
what is going on, and you can always replace the enum typedef with
a typedef for (int) and the appropriate #define's instead.

While we're on the subject, I also have zero use for the 'void' type
in C.  I never could figure out why it was needed in the first place.
I just use:

	typedef int VOID;
	# ifdef lint
	 VOID __void__;
	# define IGNORE (X)	__void__ = (VOID) (X);
	#else
	# define IGNORE (X)	X
	#endif

and then in the code:

	IGNORE (fclose (stream));

It does the job to keep lint happy, and declares in the source that
you at least think you know what you're doing.  It also works on
any C compiler I've used recently.  The only problem I've encountered
is one preprocessor wouldn't let you continue a macro on another line,
so if you try to keep everything to 80 columns:

	< many nested loops and conditionals ...>

						IGNORE (function (with,
							lots_of, args,
							that, wont_fit,
							ona_line));

it barfs, but it seems to me that qualifies as a genuine preprocessor
bug and shouldn't constrain one in the general case.

The problem with the true 'void' type (at least on 4.1bsd) is:

	void func () { ; }
	int ifunc () { ; }

	test  () {
		void (*funcp)();
		int (*ifuncp)();
/*###67 [cc] void type for ugottabekiding %%%*/
/*###67 [cc] unknown size %%%*/
		void ugottabekiding;
		
/*###69 [cc] operands of = have incompatible types %%%*/
		funcp = func;
/*###70 [cc] operands of = have incompatible types %%%*/
		ifuncp = func;
/*###71 [cc] warning: illegal pointer combination %%%*/
		funcp = ifunc;
/*###72 [cc] ugottabekiding undefined %%%*/
	}

Now it seems reasonable to disallow the 'void' type to declare
identifiers ("ugottabekidding"), but the whole point of it is to
properly declare functions which don't return values.  Note that the
declaration "void (*funcp)();" is legal -- so how come I can't use it?
Anyway you try you lose: you can't use the name "func" for anything --
it's a hard error.  If you try to use "ifunc" instead it draws a
warning.

Just to balance this gripe a bit, the other change that went into C
with 'enum' was structure and union assignment, which is a big win,
especially for yacc parsers.  Another more recent change is the rule
that each structure type gets its own symbol table so:

	struct FOO {
		struct FOO *next;
		int bar;
	};

	struct BAR {
		int foo;
		struct BAR *next;
		int notinfoo;
	};

no longer gives you the "next: multiply defined" error.  This
considerably reduces the imagination required to keep all the
structure members uniquely named in a large system.  This also
has the by-product of enforcing stricter member usage:

	struct FOO *foop;

	foop->notinfoo = 0;

is now illegal.

	- Alan S. Watt




More information about the Comp.lang.c mailing list