Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Enumerations


May 04: Enumerations

"Blech!" At least, I think that's the sound Wendy made. There was also a small gurgle. I decided I could spare a few moments, pushed back from my keyboard, and walked over to her cubicle. "Wassup, pardner?" I asked.

She muttered something I didn't quite catch, though I thought "stinkin' Bahb" was part of it. Then, more clearly, she added: "But there just can't be anything wrong with this!"

"Let me see." She pointed at her editor window, where the cursor was on a line that read:

// check state of traffic light
if( lampColor == Green ) {   

"I'm working on one of the new features in the traffic simulations," Wendy explained. "I was actually working on something else, but the program started misbehaving, and I narrowed it down to this line here. I don't get it — this line was working fine, and now suddenly it still compiles but the condition is never true any more. I never go into the body of the if statement even when lampColor is definitely Green."

"You stepped through it in the debugger?"

"Yep."

"What's the declaration of lampColor?" I asked.

"It's getting passed in from another module." She scrolled up, and I saw that it was a parameter to the function:

void f( int lampColor, /*...*/ ) {

"Okay, then. What's Green?"

She did a quick search, but that was the only occurrence of Green in the file. "It's just an enumerator, getting pulled in from a header. It's legal to compare to an int."

"Sure," I agreed. "It compiles after all, it just does the wrong thing when you run it. Say, did you check the history of the file?"

"No, why?"

"Who edited it last?"

Wendy smacked herself in the forehead. She did it pretty hard; I wondered if it hurt. "Arrgh!" she arrghed, scrolling to that part of the file. "That's it! It must have been Bahb! Of all the stinkin' toady misbegotten boorish insensitive churlish ignorant..." She continued at impressive length, and she was still going strong on the long trail of adjectives when the answer appeared and we saw:

// $Version 1.45$: $Author: KerryK$

We both fell silent.

"It wasn't Bob," I offered helpfully.

"Well, I can see it wasn't Bahb," Wendy retorted, waving at the monitor.

"Well, what's the diff?"

Wendy typed a few commands, and then discovered that Kerry's change included the following replacement:

--- #include <lightstate.h>
+++ #include <colors.h>

A low growl began deep in Wendy's throat. I watched with interest as she pulled up the two headers and finally compared two declarations side by side:

// file lightstate.h   // file colors.h  
//		               //                     
enum LightState {      enum Colors {         
  Red,                  Red,                 
  Amber,                Green,               
  Green                 Blue,                
};                      Purple,              
                        Violet,              
                        // ...               
                      };                     

Wendy's growl grew in intensity and deepened. Then it stopped, and she was quiet for a while.

Finally, Wendy got up and started walking over to Kerry's cubicle. I followed, trying to hide my smile. During the short walk, the Guru silently appeared and joined us, this time startling no one; I suspected that she was also suppressing a smile, judging from the glint in her eye.

Kerry was working at his desk, but looked up at the three silent faces staring down at him.

"Hello, Kerry," Wendy said sweetly. The Guru and I only watched the exchange with interest.

"Hi, uh, Wendy," our little intern replied.

"You did some work on the traffic system, huh?"

"Yes...?"

"Some nice additions there. Nice job."

"Uh, thanks." Kerry was still glancing from one of us to the other, but we gave him no sign.

"Your additions worked fine, then?" Wendy continued, still sweetly.

"Yeah, I, uh, tried my new unit tests and everything. They worked fine."

"And all the other unit tests, they all passed too?"

"I, uh, did a sanity check with some of them, yes. Everything seemed to be okay."

"And that was enough for you to do a check-in, you dog?!" She started to step forward, but the Guru and I were each holding one of her arms, and easily held her back, smiling calmly. Wendy took the hint, and we let her go again. She straightened her outfit. "Er, sorry, Kerry. Let me try that again. What possessed you to substitute one of the header files?"

"Oh, that," Kerry responded, a light beginning to dawn. "Well, for the new functionality I added, I needed the Colors enumeration, so I included that file. But then I was getting compile errors because it conflicted with some other enum..."

"LightState?" Wendy supplied helpfully.

"Uh, yeah, LightState, that was it. The two couldn't coexist, so I commented out the LightState header to see if it was being used. Everything compiled fine, and I ran all my new tests and a few of the old ones, and those ran fine, too. So, since obviously LightState wasn't needed, I left it that way and checked it all back in."

Wendy looked at me. "Dish duty?"

I nodded. "Seems fair. One week?" I looked at the Guru.

"One week," the Guru agreed, and looked at Kerry. "My child, you have been tempted by the dark path. We must correct you. Dish duty for one week shall be your punishment."

"Okay, but what did I do wrong?"

Wendy pulled up the two headers and pointed at the difference. "Both enums have an enumerator named Green, but they have different values; LightState's Green has the value 2, but Colors' Green has the value 1. With me so far?"

"Uh, yes," Kerry said. "Because their values aren't specified, they default to 0, 1, 2, and on. Right?"

"Right. Now, do you see why the two Greens conflicted when you had both headers included?"

"No, I don't understand that. Aren't the enums different scopes?"

"Nope," I piped up, taking my turn. "More's the pity. An enum doesn't introduce a new scope; the enumerators are all in the same scope as the enum itself. So the two enums couldn't coexist in the same scope."

"But when I commented out the LightState definition, everything seemed to work...?" Kerry trailed off, thinking.

"'Seemed to,' right," Wendy said, mellowing a little and taking some pity on the poor intern. "As it turns out, the Amber enumerator that was unique to LightState wasn't being used in this file, so when you commented it out, the compile still succeeded. Only it used the wrong Green, of course. Maybe it used the wrong Red somewhere, too; I didn't check to see if Red was already being used. There may be another breaking bug we didn't discover yet. Fortunately, Red's value is the same in both places — "

"Or maybe unfortunately," I put in an oar, "because it'll just mask a logical error and it'll break later if the two Reds diverge."

"Oh." Kerry appeared chastened and quite glum.

"All right, okay, I guess this isn't so much your fault — though you should still have run all the tests," Wendy added pointedly. "But accidents will happen, because it's true that enums aren't strongly type-safe."

"In this and other ways," the Guru agreed, finally engaging. "As the Father of All Gurus, the Great Prophet Stroustrup, once penned: 'C enumerations constitute a curiously half-baked concept.'"[1]

"C enums are just ints in drag," I quipped. "C++ enums are still ints in drag, but with better makeup."

Kerry looked confused.

"C enums are essentially ints," the Guru amplified, "and you can freely assign between enums and ints, and between unrelated enums. Consider this parable, which is perfectly legal C:" [2]

// Example 1: Compiled as C
//
enum LightState { Red, Amber, Green };
enum Other { Foo = 42, Bar, Baz };
int main() {
  enum Other o = Foo;
  enum LightState l = Red;
  o = Red;         // ok in C: but very questionable
  l = Foo;         // ok in C: but very questionable
  o = l;           // ok in C: but very questionable
  l = 99;          // ok in C: but very questionable
  if ( o == l ) ;  // ok in C: but very questionable
}

"The last five lines are deplorably type-unsafe," she said sadly.

"Why?" Kerry asked gamely.

"Oh, where to begin? They assign values that the enumerations have no enumerators to represent and probably were never intended to hold, they assign between unrelated enumerations, they compare unrelated enumerations, and the worst part of it is..." she trailed off.

Kerry held out for a few seconds, then broke down and offered the straight line she was waiting for: "What's the worst part?"

The Guru smiled, and finished: "The worst part of it is that only some of the questionable writings are errors in C++." [3] She amended the example:

// Example 1: Compiled as C++
//
enum LightState { Red, Amber, Green };
enum Other { Foo = 42, Bar, Baz };
int main() {
  enum Other o = Foo;
  enum LightState l = Red;
  o = Red;        // error in C++
  l = Foo;        // error in C++
  o = l;          // error in C++
  l = 99;         // error in C++
  if ( o == l ) ; // still ok in C++: but still very questionable
}

"And," the Guru added, "you still cannot declare Colors and LightState in the same scope because the names of their enumerators still are in the enclosing scope."

"If I might have permission to restate what you just said...?" Kerry asked sheepishly. The Guru nodded, smiling. "Then I would say: C++'s enums do have improvements over C's, but even C++ enums still have type-safety holes you could drive a Mack truck through."

We all smiled. Even Wendy had calmed right back down.

"Indeed," said the Guru. "Even in C++, enum values have an implicit conversion to int, so you can compare unrelated enumerations. Even in C++, enums do not introduce a real scope, so their enumerators conflict. Even in C++, the programmer cannot specify the exact underlying type of the enum, and so there can be unpredictable behavior, such as in this writing:" [4]

// Example 2
//
enum E { E1 = 1, E2 = 2, Ebig = 0xFFFFFFF0U };    // note: U suffix
int main() {
  cout	<< sizeof( E ) << endl;
  cout	<< "Ebig = " << Ebig << endl;
  cout	<< "E1 ? -1 =\t"
	    << ( E1 < -1 ? "less" : E1 > -1 ? "greater" : "equal" )
	    << endl;
  cout  << "Ebig ? -1 =\t"
	    << ( Ebig < -1 ? "less" : Ebig > -1 ? "greater" : "equal" )
	    << endl;
}

"What output should this program produce? Do not answer — merely try this on some of the compilers that we use," she said, seeming amused and quite satisfied with herself, "and see what results you get." (We later did, on a platform where sizeof(E) was always 4, and observed the results in Table 1.)

"But how should we fix the file that had the problem?" Wendy asked. "We can't have both enums in there because Red and Green conflict."

"You must refactor it in two files," the Guru said, "or else change one of the enums to make it more type-safe, but that would force you to update all users of the enum. If you choose to do that, you can write at minimum something like this..." She wrote:

class Color {
  enum Color_ { Red_, Green_, Blue_, Purple_, Violet_ };
  Color_ value;
public:
  static const Color Red, Green, Blue, Purple, Violet;
  explicit Color( Color& other )     
    : value( other.value ) { }
  bool operator<( Color const& other )    
    { return value < other.value; }
  bool operator==( Color const& other )     
    { return value == other.value; }
    // etc.
  int ToInt() const     { return value; }
};
const Color Color::Red( Color::Red_ );
const Color Color::Green( Color::Green_ );
const Color Color::Blue( Color::Blue_ );
const Color Color::Purple( Color::Purple_ );
const Color Color::Violet( Color::Violet_ );

"This is a true type-safe class: The conversion to int is now explicit with ToInt(), the enumerators are now scoped and must be referred to as Color::Green, Color::Green cannot be accidentally compared with a LightState, and so on."

"What was that term, the one Stroustrup used to describe enumerations?" Kerry asked.

We all smiled. "Half-baked," we said in unison, and the Guru added, "but C++'s are only partly better. But it may perhaps be, one day in our future, that C++ will have more strongly type-safe enums, and the world will be a better place...and a safer one, even for programmers like Bob." [4,5]

References

[1] B. Stroustrup. The Design and Evolution of C++ (Addison-Wesley, 1994), p. 253.

[2] ISO/IEC 9899:1999(E), Programming Languages — C.

[3] ISO/IEC 14882:2003(E), Programming Languages — C++.

[4] D. Miller and H. Sutter. "Strongly Typed Enums," ISO C++ committee paper ISO/IEC JTC1/SC22/WG21/N1579, February 2004.

[5] D. Miller. "Improving Enumeration Types," ISO C++ committee paper ISO/IEC JTC1/SC22/WG21/N1513, September 2003.


Herb Sutter (http://www.gotw.ca/) is convener of the ISO C++ Standards committee, author of Exceptional C++ and More Exceptional C++, and Visual C++ architect for Microsoft.

Jim Hyslop is a senior software designer for Leitch Technology International. He can be reached at [email protected].



Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.