Name: _____________________ Class: CET 375
SSN/ID:   _____________________ Section & Group: ____________
Enumerations and Compilation


One view of object oriented programming (OOP) is programming using data types to model real-world objects; that is, defining data objects to model "real-world" objects and operating on those objects to perform a computation. The problem is that the pre-defined C++ types (int, double, char, etc.) are fine for modeling numeric data and some text data but many real-world objects are not numbers or characters. We want to create new data types to model such non-alphanumeric objects. The objective of this lab exercise is to show how it is possible to extend the programming language C++ by adding these new data types to it.

In this lab exercise, you will be using the program source_lab04_geology.cpp. Please download this file to your home directory and rename it to geology.cpp.

Problem: To model and process rocks (basalt, dolomite, granite, limestone, marble, obsidian, quartzite, and sandstone).

Approach #1 (the "old-fashioned" approach)

Code each rock with an integer code and use int to model rocks.

int rock1, rock2;

// Set rock1 to basalt, rock2 to quartzite
rock1 = 0;
rock2 = 6;
Difficulties with this approach: Approach #2: Define distinct named constants

Look at the program geology.cpp to see what it does. Note the sequence of const declarations. Compile the program and execute it.

  1. What happens if you enter the name of a rock such as BASALT?




  2. Execute the program and enter the numeric codes of basalt, dolomite, and granite. Describe the output produced:




Note: We really haven't gained much over Approach #1 — we have to input numeric codes and numeric codes are output.

Approach #3: Use an enumerated type.

The problem in the preceding approach is that we are representing real-world objects (rocks) with integer codes (0, 1, 2, ...) instead of using their "real-world" names (basalt, dolomite, granite, ...). C++ provides an enumeration mechanism that allows a programmer to define a new data type whose values are those real-world names. In this lab exercise, we will create a new data type Rock whose values are the names of the rocks, instead of integers.

A Brief Summary of Enumerations

In earlier programming languages, it was necessary to use numeric codes to represent real-world objects; for example, using 0, 1, 2, 3, ... to represent rocks instead of using their real-world names (asalt, dolomite, granite, limestone, ...). The problem with this is the difficulty of remembering these codes and reading instructions for processing objects using these codes. C++ provides the enumeration mechanism that allows a programmer to define a new data type whose values are those real-world names.

The following is an enumeration declaration for a new data type Color whose values (called enumerators) are the colors RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, and VIOLET followed by a COLOR_OVERFLOW value:

enum Color {RED, ORANGE, YELLOW, GREEN,
            BLUE, INDIGO, VIOLET, COLOR_OVERFLOW};
This declaration defines a new type, named Color, whose values are the identifiers listed between the curly braces. (These enumerators must be valid C++ identifiers.) When the compiler encounters such a declaration, it performs an enumerator-to-integer mapping referred to earlier, associating 0 with the first enumerator in this list, 1 with the second, and so on. Thus, for the preceding declaration, the compiler associates 0 with RED, 1 with ORANGE, 2 with YELLOW, ..., 6 with VIOLET, and 7 with COLOR_OVERFLOW.

As another example, the declaration

enum Gender {FEMALE, MALE};
declares a new type Gender whose values are the identifiers FEMALE and MALE; the compiler will associate 0 with FEMALE and 1 with MALE. Similarly, the declaration
enum HandTool {HAMMER, PLIERS, SAW, SCREW_DRIVER};
constructs a new type HandTool whose values are HAMMER, PLIERS, SAW, and SCREW_DRIVER and associates the integers 0, 1, 2, 3 with these identifiers, respectively.

C++ also allows the programmer to specify explicitly the values given to the enumerators. For example, the declaration

enum NumberBase {BINARY = 2, OCTAL = 8, DECIMAL = 10,
                 HEX = 16, HEXADECIMAL = 16};
associates the identifiers BINARY, OCTAL, DECIMAL, HEX, AND HEXADECIMAL with the values 2, 8, 10, 16, and 16, respectively. Similarly, if we wished to have the values 1, 2, ..., 7 associated with the seven colors given earlier (instead of 0 through 6), we could use the declaration
enum Color {RED = 1, ORANGE = 2, YELLOW = 3, GREEN = 4,
            BLUE = 5, INDIGO = 6, VIOLET = 7, COLOR_OVERFLOW = 8};
or, more compactly,
enum Color {RED = 1, ORANGE, YELLOW, GREEN,
            BLUE, INDIGO, VIOLET, COLOR_OVERFLOW};
because the integer associated with an enumerator is, by default, one more than the integer associated with the preceding enumerator.
  1. In the space below, write an enumeration declaration for a new data type Rock whose values (enumerators) are the rock names BASALT, DOLOMITE, GRANITE, LIMESTONE, MARBLE, OBSIDIAN, QUARTZITE, and SANDSTONE, followed by a ROCK_OVERFLOW value.




  2. Now modify Part 1 of program geology.cpp as follows:
    → Replace the 9 const defniitions with this enumeration declaration.
    → Change the type of sample from int to Rock.
    Attempt to recompile and execute the program. describe any errors that occur.




    As the error messages should indicate, one difficulty is that the input operator >> is not defined for our new type Rock. This will be fixed later in the lab exercise but, for the time being, make the following "quick-and-dirty" patch:

    Replace the offending statement

        cin >> sample;
        
    with:
        int temp; cin >> temp; sample = Rock(temp);
        
  3. Recompile and execute the program as before, entering the numeric codes of basalt, dolomite, and granite. Describe any differences between this program's output and that in Approach 2.




  4. Now modify Part 2 of program geology.cpp as follows:
    → Change the type of rockVal in the second for loop from int to Rock.
    Attempt to recompile and execute the program. Describe any errors that occur.




    As the error messages should indicate, the difficulty is that the ++ operator (either in postfix or prefix form) is not defined for our new type Rock. We will remedy this problem shortly.
In constructing our new data type Rock, it probably seems that we haven't improved at all on Approach #1, and that, in fact, we have made it worse. The reason for this is that:

A data type has two components:
1. A collection of values
2. Operations on those values
Our enum declaration defines the values of type Rock; but this is only a small part of what we need to do. We must define functions to operate on those values. So that this new type is reusable, we will package all the parts together in a library. (Later we will make it a class.) This library will consist of two files: a header (or declaration) file and an implementation (or definition) file.

Header file

The header file is the interface between the data type and the user, so it must be documented thoroughly. It's opening documentation should include (at least) the following items (and other items from the Programming Style Sheet):

Then follow:
  1. Create a header file rock.h containing:
    → Opening documentation (items listed above and in the Programming Style Sheet, e.g.)
    → The Rock enumeration declaration
    Note: Since some of the functions will use the types istream and ostream, put:
        #include <iostream>
        using namespace std;
        
    directives at the beginning of this header file.
  2. Now add:
        #include "rock.h"
        
    at the beginning of geology.cpp and remove the enumeration declaration of type Rock. Recompile geology.cpp to check that there are no new compilation errors — only the ++ error from 6.
Defining a Successor Operation

We will first consider the problem in the for loop in Part 2 of geology.cpp:

for (Rock rockVal = BASALT; rockVal <= SANDSTONE; rockVal++)
    cout << rockVal << " ";
(Note: in C, you need to declare Rock rockVal as enum Rock rockVal.)

The problem is that the increment operator ++ is defined for integers but not for Rocks. You will now construct a function Next() that returns the successor of a rock and use it in place of the ++ operation. (In Lab 4 we will see how the operator ++ can be overloaded for type Rock so that this for loop will execute.)

  1. Modify the for statement in Part 2 of geology.cpp as follows (so it can be used to test Next()):
        for (Rock rockVal = BASALT; rockVal <= SANDSTONE;
                                               rockVal = Next(rockVal))
            cout << rockVal << " ";
        
    A specification for Next() is as follows:
    Receive:
    A Rock value rockVal
    Return:
    The successor of rockVal (the successor of the last enumerator SANDSTONE is ROCK_OVERFLOW)
  2. Add the following function prototype to rock.h:
        Rock Next(Rock rockVal);
        
    and appropriate documentation (as per the Programming Style Sheet).

    → Each function prototype should be accompanied by a block comment giving the following:

    → Include meaningful parameter names in the function prototypes (rather than simply type names) as this also helps describe how to use the function.
Implementation File

The implementation file contains:

→ The definitions of the operations (functions) on the new type.
→ These are preceded by a #include directive to insert the library's header file (so the compiler can check that the prototypes and definitions match).
  1. Complete the following definition of Next() and put it in rock.cpp:
        Rock Next(Rock rockVal) {
          switch (rockVal) {
            case BASALT:     return DOLOMITE;
            case DOLOMITE:   return GRANITE;
            . . .
            default:
              // Output an error message and return the ROCK_OVERFLOW value
           }
        }
        
Be sure to include opening documentation in the implementation file like that in the header file. You need only document each function with a short description of the operation performed by that function. (Usually, this can be just a one-line comment.) Please refer to the Programming Style Sheet.

Compiling and Linking

The directive #include "rock.h" causes the compiler (actually, the preprocessor) to insert the declaration of Rock and the prototype for function Next() into geology.cpp. However, the definition of Next() is in rock.cpp, not in geology.cpp.

Translating a program like geology.cpp to produce a binary executable program involves two phases:

  1. Compiling, in which C++ code is translated into machine language; and
  2. Linking, in which calls to functions outside the main file are linked to their definitions.
All implementation files being used must be compiled as well as the main program. The resulting object files must then be linked together to produce a binary executable.

Step 1:
Separately compile the program file name.cpp to produce an object file named name.o in some systems. This may require using a special compiler switch, as in the GNU C++ command:
      g++ -c geology.cpp
      
geology.cpp geology.o
Step 2:
Separately compile ech library implementation file in the same manner; for example:
      g++ -c rock.cpp
      
rock.cpp rock.o
Step 3:
Link the object files together into a binary executable program — for example, in GNU C++ with the command:
      g++ geology.o rock.o -o geology.exe
      
geology.o geology.exe
rock.o
In some systems, these steps are carried out automatically by selecting the appropriate menu command — for example, Build geology.exe on the Build menu in Microsoft's Visual C++ and the Make or (Run) command on the Project menu in Metrowerks' CodeWarrior C++.

If a program involves several different libraries, it can be very difficult to keep track of which files are out of date, which need to be recompiled, and so on. Single commands like the preceding that do this automatically are very usefull in this regard. With GNU C++, UNIX's make utility can be used to execute a file named Makefile that contains the commands for the compilations and linking.

Defining a Rock Kind Operation

  1. Add another function Kind() to type Rock that returns one of the strings "igneous", "metamorphic", or "sedimentary" that indicates the kind of a rock, using the following information:
  2. Add a for loop in geology.cpp to test drive this function by displaying the kinds of all the rocks. Compile, link, and execute the modified program to ensure that Kind() works.

Hand In:

  1. This lab handout with the answers filled in
  2. Listings of rock.h and rock.cpp (use the enscript command from your Programming Style Sheet:
    enscript -E -G -2rj -M Letter -PECT2_PS <filename>. Or, you can use the a2ps command: a2ps <filename>)
  3. A listing of geology.cpp (use the enscript command from your Programming Style Sheet:
    enscript -E -G -2rj -M Letter -PECT2_PS geology.cpp. Or, you can use the a2ps command: a2ps geology.cpp))
  4. A demonstration that everything compiles and links correctly
  5. A sample run of geology.cpp
Category Points Possible Points Received
Completion of Lab Handout (45) __________
Driver Program (25)
   Correctness (including following instructions) 20 __________
   Style/Readability/Documentation 5 __________
Header File (35)
   Opening Documentation 10 __________
   Specifications 10 __________
   Style/Readability 5 __________
   Enumeration Declaration and Function Prototypes 10 __________
Implementation File (30)
   Correctness 25 __________
   Style/Readability 5 __________
Adequate Testing of Library (15) __________


TOTAL

150

__________


Ricky J. Sethi <rickys at sethi.org>
Last modified: Mon Mar 28 18:33:02 PST 2005