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:
-
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.
-
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.
-
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:
-
driver.cpp compiled, producing driver.o;
-
metric.cpp compiled, producing metric.o; and
-
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 |