Journal Articles
Browse in : |
All
> Journals
> CVu
> 113
(22)
All > Topics > Programming (877) Any of these categories - All of these categories |
Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.
Title: All You Need to Know About enums
Author: Administrator
Date: 03 April 1999 13:15:30 +01:00 or Sat, 03 April 1999 13:15:30 +01:00
Summary:
Body:
I am preparing the appendices for a book that I am writing and thought that reading them might be beneficial to some members and that others might be able to provide constructive criticism.
C for reasons best known to its designers has a special namespace - tag space - for the tags that come between the keywords that introduce a user defined type and the opening brace of the definition. This means that you have to use typedef to create a typename for an enum (as well as for struct and union types).
The second feature of C is that an enum will have an underlying integer type (implementation defined) to provide its values and that all the 'legal' values of the underlying type will also be 'legal' values for the enum type. There is a requirement that effectively constrains the underlying type to be an int or convertible to an int without loss of data.
The enumeration constants of an enum are compile time constants. The values are either declared by explicit initialisation, or implicitly as 1 more than the preceding item in the enumeration list. If not given an explicit value the first enumerated constant in the enumeration list is zero.
Here are some examples to help clarify the text:
enum Wheels { bicycle = 2, motorcycle = 2, tricycle = 3, car = 4}; enum Index { zero, one, two, three, four}; enum Colour {red=1, green, blue=4};
'Wheels', 'Index' and 'Colour' are not type names in C, they are just tag names so they must be prefixed with enum wherever they are used. However tag names do not conflict with other non-tag uses of the same 'name' in the same scope. So we can write:
typedef enum Wheels Wheels;
which means that the second use of 'Wheels' in that declaration is an ordinary typename for the type identified by enum Wheels. C programmers will insist on writing things like that with the result that novices get totally confused.
That is about it. In other words enums in C are very weak user defined types. None the less they can be very useful. For example they are, in my opinion, a better option for providing compile time integer constants than the more traditional method via the preprocessor. In my opinion #define should only be used to provide integer constants for use by the preprocessor. So I have no problem with:
#define MSDOS 1 #if MSDOS #include <dos.h> #endif
But I have serious reservations over:
#define MAXSIZE 100 char buffer[MAXSIZE];
and would prefer:
struct ProgramLimits{ mazsize = 100}; char buffer[maxsize];
However I suspect that the traditionalists will argue strongly for the preprocessor solution.
Another place where enums are useful in C are for the control values for a switch statement. For example consider a menu where the selection is controlled by a switch. Which of the following is easier to maintain:
switch(tolower(getc(stdin))){ case 'R' : getdata(); break; case 'W' : writedata(); break; case 'Q' : exit(); }
or
enum {read_in = 'r', write_out='w', quit = 'q'}; switch(tolower(getc(stdin))){ case read_in : getdata(); break; case write_out : writedata(); break; case quit : exit(); }
Not convinced? Well which will be easier to convert to handle a menu that is being displayed in French though it has the same functionality? The traditional way of using a switch to select on the basis of a response to a menu is highly dependant on the natural language being used to display messages. We cannot remove this dependence but we can try to localise it.
If I understand the C rules correctly, C provides an implicit conversion between enum values and ints in both directions. In other words C has no great problem with:
enum X { zero, one two }; enum X x=0;
In general C++ tries to ensure that code that is valid C will remain valid C++, however sometimes this seems to be unreasonable (perhaps because C is taking, in the eyes of the designers of C++, one liberty too many). In the case of enums the designers of C++ considered the implicit cast from int to a enum type to be one of these cases. In C++ there is no implicit conversion from any other type to any specific enum type. That means that you can only assign a value of the correct enumerated type to an enum variable. If you want to do something else you must use a cast.
In addition, there are no built-in arithmetic operators for enumerated types, though programmers can provide their own. (Note however, that you cannot provide an assignment for an enum, the built-in bitwise copy is all you have). So we have:
enum Colour {Black, Red, Green, Blue=4, White=7}; int main(){ Colour c0=Black, c1=Green, c2; c2 = c0 + c1; // error no conversion from int to Colour c2 = Colour(c0+c1); }
Note the further difference between C and C++. C++ has no tag space (actually for backward compatibility it allows an awful hack, if the same name is used for a typename and an identifier name enum (struct, union or class) can be used to disambiguate. Never willingly do this but you may have to use it when calling third party code.) What were tags in C have grown up to become full typenames in C++ so we neither need a typedef nor an enum prefix to use Colour as a full user defined type.
The next feature is that when you do arithmetic on enums the default behaviour is to convert them to ints. So in the above code c0 + c1 is computed with int arithmetic and so evaluates to an int. Because there is no implicit conversion available, you then have to explicitly create a Colour value from the int value. In theory you could provide overloaded arithmetic operators, but the lack of an implicit conversion means that we would have to provide three versions of each to cover all the bases:
Colour operator + (Colour, Colour); Colour operator + (int, Colour); Colour operator + (Colour, int);
I doubt that many would think this worth the effort. Generally we do not use enums for arithmetic. See the text box for more detail on this.
Another difference between C and C++ enums is in the range of values that must be supported. In C all values of the underlying type (if only you knew what that was - read the compiler documentation for the answer). C++ is much more restrictive. You must determine the number of bits that are required to represent (in binary) the enumerated constants (I am still unclear if an implementor is permitted to use some scheme of base+offset, I think not but I may be wrong). This opens up the potential for packing enums in an array. In practice I have never come across a compiler that does anything different in C++ from what you get in C. However, strictly speaking in the context of:
typedef enum Colour{Black, Red, Green, Blue=4, White=7} Colour;
The following is valid in C but undefined in C++:
Colour c = 255;
Because 127 requires 7 bits while White only requires 3. But the smallest underlying type is char and that must be able to represent 127.
There are differences between C and C++ language specifications of enum. However with the exception of the naming rules (tags versus typenames) and the rule about implicit conversion from int to an enum you are unlikely to ever need to know them in practice (though they make silly test questions and good quiz questions for language lawyers.)
While enums can be used to provide extra integer types (superficially useful in C++ where they can be used for overloading) actually using them for arithmetic is probably unwise.
enums are a good mechanism for providing portable (between C and C++) compile time constants and are certainly superior to the older mechanism via the preprocessor. However in the scope of a class or namespace const qualified values are probably better style (no use for portability between C and C++ though because const values are not compile time constants in C)
Finally in C++ enums are an excellent tool for knocking off quick, simple exception types. If you do not want any associated information they can even be empty:
enum MyException {};
Now what have I left out?
Notes:
More fields may be available via dynamicdata ..