course.wilkes.edu/CS125Labs
Welcome to the site for CS 125 labs!

 

Part II

Writing a Program to Test the Library

Next, open driver.cpp and personalize its documentation. In this file we will create a program whose sole purpose is to test FeetToMeters(). Such programs are called driver programs, because all they do is "test-drive" the functions in a library.

Designing The Driver Program

Most driver programs use the same algorithm:

   1. Display a prompt for whatever values the function requires as arguments.
   2. Read those values from cin.
   3. Display via cout the result of calling the function,
       using the input values as arguments (plus a descriptive label).
Since every driver program uses this same basic algorithm, we can skip the entire Design stage and proceed straight to Coding.

Coding the Driver Program

Personalize the opening documentation in driver.cpp. Then add a C++ program containing the necessary output, declaration, and input statements to perform steps 1 and 2 of our algorithm. Don't forget to add #include directives to insert the header files of both the iostream library and our metric library.

To perform step 3, recall that an argument is a value that is passed to a function when the function is called. For example, if we were to write:

   cout << "\nThe number of meters is " << FeetToMeters(inValue) << endl;
then the value of inValue would be the argument in this call to FeetToMeters().

There are three important rules to remember when calling a function:

  1. The number of arguments in a function call must equal the number of parameters in the function's prototype, or a compilation error will occur.
  2. The number of arguments in a function call must equal the number of parameters in the function's definition, or a linking error will occur.
  3. The types of the first argument and first parameter, the second argument and second parameter, the third argument and third parameter, etc. must be the same, or a compilation error will occur.
So if your program compiles correctly but fails to link, you can infer that there is a mismatch between the parameters in your function's prototype and definition.

Multi-File Translation

Since our program involves more than one file (i.e., driver.cpp and metric.cpp), translating it becomes slightly more complicated. When a program consists of multiple files, the easiest way to coordinate the translation is to use a special UNIX utility named make (where have you seen that before?). To use make, we must prepare a special file called a Makefile. As we shall see, this file coordinates the compilation of the source files and the linking of the object files. You should have already saved a copy of the partial Makefile for this program; the next sections describe how to complete it.

Makefile Structure

Open the file named Makefile. A simple Makefile consists of a series of pairs of lines, in which the first line of the pair names the file being made (called the target), followed by a colon (:), followed by the files from which it is made (called the components). The second line of the pair consists of a TAB character, followed by a command that makes the target from the components, followed by a Return (or Enter).

For example, our binary executable program driver is made by linking together the object files driver.o and metric.o, so the first pair in our Makefile is as follows:

   driver: driver.o metric.o
           g++ driver.o metric.o -o driver
In the same fashion, our driver program's object file driver.o is made from driver.cpp, along with metric.h (which driver.cpp</T> #include-s). Since driver.o is made by having g++ compile driver.cpp with the -c switch, this pair appears as follows:
   driver.o: driver.cpp metric.h
           g++ -c driver.cpp
(Note: Our browser replaced the TAB character before each g++ with spaces. If yours does the same, you will need to delete those spaces and replace them with a TAB.)

A similar pair of lines is used to specify the way that metric.o is made from metric.cpp and metric.h. At the end of your Makefile, construct such a pair. (Don't forget the TAB at the beginning of the second line, or the Return at the end of the line -- these are the most common errors when creating a Makefile.) When you have added this pair, we are ready to translate our program.

To test your Makefile, first remove metric.o from your working directory. Then translate your program with the compile command, but use make instead of calling g++ directly:

   Compile command: make
As you can probably deduce, this will invoke a program named make instead of g++. The make program reads your Makefile and tries to make the first target it encounters (driver). However, make cannot create driver until both driver.o and metric.o exist, and so it must make each of them in turn. Since driver.o is listed first in the component list, make jumps to the pair that tells it how to make driver.o. Since driver.o depends on driver.cpp and metric.h, and both of these exist, make executes the second line in this pair:
   g++ -c driver.cpp
which creates driver.o, if driver.cpp contains no syntax errors.

Once driver.o exists, make jumps to the pair that tells it how to make metric.o (i.e., the pair of lines you just wrote). If you wrote the pair correctly, then you should observe the second line of that pair execute, creating metric.o.

Once driver.o and metric.o exist, everything needed to build the target driver is finally available, and so make returns to the first pair and executes its second line:

   g++ driver.o metric.o -o driver
This produces the binary executable driver, finishing the translation.

Testing

Once driver is successfully translated, we are ready to test what we have written, to see if we can discover any logic errors. Execute driver, using the following sample data, to test FeetToMeters() correctness:

Feet Meters
(Predicted)
Meters
(Observed)
1.0 0.3048 ______________
3.3 1.00584 ______________

Since FeetToMeters() is a linear function, two test values are sufficient to verify its correctness. Be sure to note and correct any discrepancies. When driver executes FeetToMeters() correctly, our task is finished!

More About make

Before we quit, let's learn a bit more about how make works. If you were watching closely when driver translated, you saw three separate commands execute:

  1. driver.cpp compiled, producing driver.o;
  2. metric.cpp compiled, producing metric.o; and
  3. driver.o and metric.o linked, producing driver.

Using your editor, alter driver.cpp in some trivial way (e.g., add a blank line at its beginning). Then use the compile command to invoke make again. Do all three steps occur again? If not, which step is omitted?

UNIX systems associate a "last modified date" with each file. To avoid recompiling a file needlessly, the make program uses these dates to determine what files need to be recompiled. That is, the date of metric.o was more recent than that of metric.cpp. The make program noted this and concluded that metric.cpp had not been modified, and so did not waste time recompiling it.

By contrast, when we made our trivial modification to driver.cpp and then saved it, the file driver.cpp became newer than the file driver.o. The make program noted this, concluded that driver.o was outdated, and so re-made driver.o. This in turn made driver.o newer than driver. Recognizing this, the make program reperformed the command to make driver, which relinked (the new) driver.o and (the old) metric.o.

In general, make will only re-make a target if any of that target's components are out of date. To see this, use make to translate driver one last time (without modifying anything) and note the output.

Another Use For make

On a final note, add the following lines at the end of your Makefile:

   clean:
           rm -f driver *.o *~ *#

Then save your Makefile, go to an xterm, make sure that labs/3a is its working directory, and use ls to examine the contents of the directory. Then enter:

   % make clean
and you should see make "clean" your directory for you! Use ls again and see how much tidier it is! If you then enter
   % make
you will see make rebuild driver for you!

Here is how it works: The added lines specify the name clean as a target to be made, with no components, so "clean" has no dependencies. When you type make clean, the make program searches for the name clean as a target. Finding the lines you just added, it executes the command we specified for making clean, which uses the rm command to remove driver, all object files, and all of the backup files xemacs creates.

If you always store each of your programming projects in a separate directory, and always supply a Makefile to translate the project, then managing your projects becomes trivial -- you simply cd to the directory where the project resides, enter make, and it will translate your project for you! Then when you are done enter make clean and make will clean up everything except your Makefile and your source files, saving disk space.

The make utility is a very powerful feature, and we have barely scratched the surface of what it can do. For more information about using make, a good book is Mastering Make (2cd Ed.), by Tondo, Nathanson and Yount (Prentice Hall, 1994), which contains information on using it on DOS-based, OS/2 and UNIX systems.

Phrases you should now understand:

Library, Function, Parameter, Argument, Driver Program.


Submit:

Hard copies of your final versions of driver.cpp, metric.cpp, metric.h, metric.doc and Makefile, plus a script file showing the execution of driver.

 
  Home

Help

Lab 0

Lab 1

Lab 2

Lab 3a

Lab 3b

Lab 4

Lab 5

Lab 6

Lab 7

Lab 8

Lab 9

Lab 10

Lab 11

Lab 12

Lab 13



Membership

Login




Last update: Tuesday, August 22, 2000 at 3:41:45 PM.
visitors to this page.