C PROGRAMMING

SD '90 and ANSI at Last

Al Stevens

This column comes to you from the Software Development '90 conference at Oakland, California. Every year, the conference gets bigger and better. I am honored this year to have been invited to participate on the Comparative Language Panel representing C and C++. More about that later.

SD '90 is the place where vendors of programmers' products -- compilers, function libraries, CASE tools, editors, debuggers, and so on -- show their stuff to the programmers who attend. Besides the product exhibits, there are workshops and lectures all week long.

The highlight of the show was a two-day session with Bjarne Stroustrup. The creator of C++ gave a wall-to-wall discussion of how to make the transition to his creation, the language that will, I believe, prevail in the '90s.

Turbo Debugger and Tools

Borland surprised us all in a press conference on Tuesday. They announced their new Turbo Debugger and Tools product, but that wasn't the surprise. The first jolt came while we sat waiting for the show to start. The crowd was moving past my seat when I realized that a German Shepherd had her head in my lap. It was Duchess, Debe Norling's seeing eye dog, patiently waiting for Debe to find a place to sit. Debe is a C freelance programmer and writer who has kindly reviewed some of my books in other publications.

The purpose of the press conference was to show some new stuff from Borland. Eugene Wong demonstrated the new Turbo Debugger, Profiler, and Assembler. He interspersed the dazzling demonstration with the expected number of cheap shots at Microsoft accompanied by the usual approving laughter from the audience. The big surprise came at the conclusion of the presentation. Eugene began to tell us about a nifty optimizing C compiler that works with the Turbo Debugger and Turbo Profiler. The audience paled when Eugene told us that the C compiler in question was Watcom C! Then, wonder of wonders, he introduced Ian McPhee, the president of Watcom who came to the podium to exhort the virtues of Watcom C 8.0.

That peculiar chain of events left the entire conference wondering if Borland, vendor of the respected Turbo C, had lost their corporate marbles. Does Gimbel's tout Macy's? It took me a couple of days to run down an "informed source" in Borland to get an explanation. David Intersimone, Borland's traveling evangelist, assured me that Borland is not surrendering the high-end C compiler market to Watcom or anyone else. It's just that they want to position the debugger and profiler as everyone's favorite tool collection, regardless of the compiler they use. Borland revealed the formats that a compiler must emit in order to be compatible, and invited all the other compiler folks to sign up. But so far Watcom is the only outfit to get on board. That's good news for professional development shops. You can use Turbo C and the Tools to develop a system and Watcom C for the final compile. Watcom's optimizer is one of the best.

What's New?

I saw a number of neat C products but not much that was new. Most vendors showed new versions of existing products. I was particularly impressed with a package called "Pro-C" that is trying to replace most of us. You interactively describe a system that uses screens and a data base and Pro-C from Vestronix (another Canadian developer) emits C code that you can tweak and compile to implement the system, reminiscent of the application generators of yore that crank out Cobol. I haven't tried it, but the demonstration was impressive.

The Comparative Language Panel

Warren Keuffel of Miller-Freeman invited me to represent C and C++ on this year's Comparative Language Panel at SD '90. The panel members each wrote a program to a specification prescribed by Warren, then used their programs to emphasize the strengths of their languages. Here's the specification.

"You are bidding for a lucrative contract with MegaBucks Corporation, developing all of their software. As part of the bidding process, MegaBucks has asked you to write a small program which demonstrates the strengths of your tools. The program, a gas mileage checker for the president of MegaBucks, accepts as (minimum) input the date, the number of gallons of gas purchased, and the price paid for the gas. Output is any meaningful graphical display or printed report which will impress the MegaBucks executives."

One of the speakers at SD '90 was Ken Orr who wrote and published a book in 1981 called Structured Requirements Definition. The book discusses the theory of output-oriented design, which says, "determine the outputs and work backwards." Warren's specification for the panel carries that idea to a new level. This is my first design that starts with any meaningful output that will impress the client.

Warren's purpose, of course, was to give us enough leeway to use our languages to their best advantage. I decided to forego fancy graphics outputs because such libraries, while available to C and C++ programmers are not intrinsic parts of the languages themselves. Instead, I used the simple standard input and output functions from both languages and wrote filter programs that read an input file and write a report.

We each had 10 minutes for our presentations. Warren sat down front and glared between us and his watch. I felt like the guy who talks fast in the TV commercials. Even though I was doing two languages, Warren wouldn't give me 20 minutes. Must be because he works for that other magazine.

Figure 1 shows the report. The trip report displays trips by automobiles showing the date, miles traveled, gasoline purchased, cost, miles per gallon, and cost per mile. Figure 2 is the input. I assumed that the system had a data entry, validation, and sorting process, and that the input would be correct and in sequence, so I built the test input file as shown in Figure 2. The first three lines are the first car description with the license number, model year, and make. Then come five-line trip records with date, odometer in, odometer out, gallons bought, and money spent. The trip records for a vehicle are terminated with an "end" record. The file is likewise terminated.

Figure 1: Gas mileage report

  1987 Corvette License # JXA283

    Date    miles  gas   cost    mpg   cost/mile
------------------------------------------------

   2-01-90    550    40   45.00    13      0.08
   2-09-90    450    30   32.50    15      0.07
  totals     1000    70   77.50    14      0.08

  1989 Dodge License # HMS001

    Date    miles  gas   cost    mpg   cost/mile
------------------------------------------------

   2-02-90    250    10   11.25    25      0.05
   2-04-90     10     0    0.00     0      0.00
   2-04-90    122     7    7.00    17      0.06
  totals      382    17   18.25    22      0.05

Figure 2: Report input

  JXA283
  1987
  Corvette

  02/01/90
  10550
  10000
  40
  45.00

  02/09/90
  11000
  10550
  30
  32.50

  end

  HMS001
  1989
  Dodge

  02/02/90
  5450
  5200
  10
  11.25

  02/04/90
  5460
  5450
  0
  0

  02/04/90
  5582
  5460
  7
  7

  end

  end

At this point I made what may be an accidental dubious historic decision. I decided to write the C++ program first. After completing the C++ program, I ported it to C. This might be the only time anyone has done that. I did it because I was too lazy to start from scratch again. All the pieces were in place in the C++ program, and all I needed to do was move them around.

This reverse exercise in inertia gave me the opportunity to observe the extent to which the C++ treatment, with its almost object-oriented flavor, would influence the appearance of the C program. Would the approach to C look different than it would have if I had started from scratch? The conclusions I drew are two-edged. The program does not look substantially different than other C programs of similar scope. The difference is in how I think about the program. I designed the C++ program by beginning with the data objects and building classes. As I thought about the algorithms that a class needed, I considered them to be methods of the class, functions that are bound to the class and not executed in any other context. The C port has those same algorithms but they are not bound to the data structures, at least not in the code. They are, however, bound together in my mind. That program is object-oriented in spirit if not in substance, but you wouldn't know it by looking at it. Strange.

Let's look at the C program first. Listing One, page 146, is auto.h, the header file that describes the data structures. You can see the C++ influence right away. All the data items have object names even if only by way of a typedef. Listing Two, page 146, is auto.c, the rest of the program. This is an unremarkable program, one that reads the input and writes the report. See if you can find much in the way of C++ influence here.

Now consider the C++ system. Listing Three, page 146, is auto.hpp, a header file that describes the classes for the program. The first notable difference between this file and auto.h is that the date structure has a display function built into it. That same function exists in the C program, but it is not bound as tightly to the structure it supports. In C you could call it, pass it anything that looked like a date, and it would display something. In C++ the only way to call it is through an instance of the date structure.

The Automobile and Trip_Record classes have the same data members that their equivalent C structures have, but they also have constructor, destructor, and other member functions. There are overloaded functions and operators and other of the C++ extensions. Listing Four, page 148, is auto.cpp, the code for the member functions. Then Listing Five, page 148, is autorpt.cpp, the code for the application.

The distribution of code is different in the C++ program. Much of what you find in the auto.c part of the C program goes into the class member functions and could be tucked away as part of a reusable class library for the programmers who work for the purveyors of the Automobile Use Tracking Output System (AUTOS. See how easy acronyms are?)

There are two questionable shortcuts in the C++ program. The memory for the instances of Automobiles and Trip_Records comes from the free store and never gets sent back. Their allocation occurs in functions that return the objects themselves rather than the pointers, so when they are ready to go out of scope, the pointer values are not around to be deleted. This apparent insanity came as the result of some as yet unresolved conflict between me and the Zortech compiler. The problem was probably mine, and the deadline for the panel approached, so I took the easy way out.

Had I not had that problem I might have needed to code an overloaded assignment function for the Automobile class. Its destructor deletes the strings that contain the license number and manufacturer. If you use objects of this class as designed in an assignment, the sending object's pointers will get deleted twice when the two objects go out of scope. A well-designed class will include an assignment function that correctly manages free store pointers in the sending and receiving objects. But because the Automobile class never gets assigned in such a way, the problem does not occur, and I did not spend the time to build an assignment function.

The point of the presentation was to stress the strengths of the languages. The strengths of C are well known. Its greatest strength is its newest, however, and that is that the standard has been approved. C is a standard language. It delivers tight, concise code, is known by many programmers, and holds the confidence of many managers. Those strengths add up to lots of jobs for programmers and almost lots of programmers to fill the jobs. C is especially suited for systems programming because of its tight code, because you can get close to the machine, and because it is loosely typed.

Don't laugh. One of the strengths of C++ is that it is strongly typed. But C++ serves different purposes with its object-oriented approach to software design and development and its facility that allows a programmer to extend the language with new data types. My favorite strength of C++ is that when the OOPS way doesn't seem to fit, I can lapse into good old reliable C.

Both languages share the advantage that when you choose them, you do not automatically sign p to a particular user interface or data base model. They are implemented on many systems with separate function and class libraries that support these things.

Other languages represented on the panel were SCHEME, Rexx, Smalltalk, and MicroStep. When the panel was over, Warren asked each of us to choose the language we would use if we needed to switch to one of the others. I had never used any of the other languages and so I chose the one I could read without already knowing it. That one was Rexx, an interpreted prototyping language.

Crotchet of the Month: Installation Programs

It was 2:00 a.m. My eyes were drooping and my jaws were locked. I hate installation programs that don't work. Zortech C++ 2.0 is a case in point. It arrived in the afternoon, and I eagerly set about to install it, anxious to use it for the exercises in a book I'm writing called Teach Yourself C++ and for the SD '90 Comparative Language Panel just mentioned. Because the book gets into some of the features of C++ 2.0, I need a 2.0 compiler to test the exercises. Just now, Zortech is the only 2.0 compiler I have.

The Zortech installation program started out bad and went downhill after that. Its first message is one about screen refreshes. That message is not the one specified in the Installation Guide as the first one. That apparent contradiction set me to tearing through the manual trying to figure where I was or if I was running the wrong program. Then, although Zortech sent me 3.5-inch diskettes, the program tells me that it is installing from 5.25-inch diskettes. That made me worry. Next, a screen that asks which disk drive I am installing on accepts my D and immediately disappears with no reassurance that it knows where it is installing. The feeling it gave was that an error had occurred, and the doubt caused me to restart several times. When at last I figured that the program must know what it was doing, it took off, asking for the compiler disks, one by one. After copying files from Compiler Diskette #4, the program asked for Compiler Diskette #5. There is no Compiler Diskette #5. The only way out of the program was to press Ctrl-Break. The installation program nicely terminated, informing me that it had done so at my request and then locked up the system. Arrgh!

Hoping beyond hope, I assumed that the compiler files I needed had been copied and that I could copy the ones from the Tools, Debugger, and Library Source diskettes. The documentation said I could. However, it turns out that the Tools diskette is not a Tools diskette at all but a copy of the Debugger diskette. I have no tools, it seems. Well, that's OK, I'll get them later. With everything else that I thought I needed in place, I loaded the ZTCHELP program. This is Zortech's on-line TSR help program after the fashion of similar programs available with Borland and Microsoft language products. It tells me, however, that it cannot find its data file, ZTC.HLP. The documentation says there should be one. I found a file named ZTCP.HLP and renamed it. Great, now ZTC can find its data base. I loaded the ZED editor, typed "printf" and pressed Shift-F1, the ZTCHELP hot key. One pretty window with much unreadable stuff in it. Stuff happens.

It's 11:00 the following morning. My eyes are wide open and my jaws are relaxed. A call to Zortech's technical support person reveals that a foul-up in the London office was the culprit and that it had been caught and corrected. He told me how to get the help system working and that my compiler is complete. They are sending me new diskettes complete with tools, and I am underway once again.

To the folks at Zortech and anyone else who builds tools for programmers: When a software developer can't get something as simple as the installation program and distribution diskettes right, it makes us wonder about the quality of the software that almost gets installed. Why do you developers feel the need to get cute with pretty windows that ask a few questions and then do nothing more than make subdirectories, copy files, and mangle our CONFIG.SYS and AUTOEXEC.BAT files? It would be OK if you could get the programs to work, but you can't, obviously, and you needn't have bothered trying. That sort of installation program is nice for poor old word processor and spreadsheet users who might not know about DOS and such, but we compiler users are programmers, for Pete's sake. Tell us what and where the files are and where to put them. And let us tell you where to put your stupid installation programs. Finally, try to get the distribution package right. Try to get all the files on the disks where you say they are. Try to get a little control on your quality.

ANSI Standard C

The ANSI Board of Standards Review approved the draft proposed standard for C as an ANSI standard in the December-January time frame. This is a monumental achievement that reflects years of hard work by a small number of dedicated souls called collectively the "X3J11 committee." The next several years will involve the committee in the task of interpreting the standard, which involves telling anyone who asks what this or that paragraph in the document really means. I wouldn't want that job.

By the time this column reaches you, the official document should be available. Its name is the "American National Standard for Programming Language C." It is formally known as X3.159-1989. It is hard to read. See what follows for two reasonable alternatives.

Paperback Writer

Don't think all those dedicated X3J11 volunteers will go uncompensated. Some of them are writing books, an activity of which I heartily approve.

The small paperback book format is popular now among publishers of computer books. Every subject imaginable is out in a "quick reference guide," which means a small book and, you hope, a small price. Such a book is Standard C by P.J. Plauger and Jim Brodie (Microsoft Press, 1989). Both authors are officers on the X3J11 committee and, as such, can be considered authorities on the subject. This book is an excellent reference manual on the syntax and function libraries of standard C. Typical of the publishing industry, the book's publication was scheduled to coincide with the approval of the standard. The approval was delayed but the book came out anyway. No matter, you'd be hard put to find any discrepancies.

I use this book as my primary reference work when I want to write a standard C program, and it hasn't let me down yet. I have two complaints, though. The book uses the "railroad track" diagram format to describe the rules of syntax. This is a format that is generally unfamiliar to most programmers unless they design languages or write compilers. The diagrams can become complex and generally unattractive, and you will find yourself retreating to the description of the diagram format so you can understand the diagrams.

My other complaint is one of format. I'd spend less time looking things up in the book if the function descriptions were organized alphabetically by function rather than by function within the name of the header file that contains the function's prototype.

Complaints aside, I can recommend this book. Every C programmer should have it.

Mutt and Jeff

The little Standard C book just discussed is a pure reference work. No tutorial. If you don't already know something about C, look elsewhere. Here is where to look. Another prominent member of the X3J11 committee is Rex Jaeschke, well-known C author, consultant, and teacher. He has written a big book called Mastering Standard C, (Professional Press, 1989). I say big book, because instead of the little paperback format, Rex has chosen the 8.5 x 11, spiral bound format for this comprehensive tutorial on standard C. Start with this one. It uses the tutorial approach, describing the subjects in easily read English and in a sequence designed to teach rather than describe. Besides, it lays flat on your desk, staying open to the page you've selected.

Of course I have a complaint. I've been looking in vain for a use for the preprocessor's ## token pasting operator. X3J11 added it to the language, and whoever dreamed it up must have had a use for it. However, none of the written matter I've seen tells me what the heck it is for. You would not expect a reference work to tell you how you might want to use some feature, but a tutorial should offer at least a glimmer, especially when the subject is as abstruse as this token pasting operator. Rex apparently doesn't understand it either. He not-so-deftly sidesteps the issue by saying that it is too advanced to be discussed.

If you want to learn the C language and are willing to spend some time learning it end-to-end, Mastering Standard C is a good way to go.

"Just Say Si"

Soon we'll stop saying "ANSI C" and start saying "Standard C." Then, when the old non-conforming compilers bite the dust, we'll drop the "standard." Maybe someday, when the two finally converge, we'll stop saying "C++" and just say C.

_C PROGRAMMING COLUMN_ by Al Stevens [LISTING ONE]



/* --------- auto.h ------------ */

/*
 * simple data element classes
 */
typedef long odometer;
typedef double Dollars;

/*
 * a simple date class
 */
typedef struct {
    int mo, da, yr;
} date;

/*
 * Automobile class
 */
typedef struct {
    int modelyear;
    char manufacturer[20];
    char license[9];
} Automobile;

/*
 * Trip_Record class
 */
typedef struct {
    date trip_date;
    odometer odometer_in;
    odometer odometer_out;
    int gallons;
    Dollars cost;
} Trip_Record;




[LISTING TWO]


/* --------- auto.c ------------ */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "auto.h"

static Automobile get_car(void);
static void reportauto(Automobile);
char *display_date(date dt);
static Trip_Record get_trip(void);
static void reporttrip(Trip_Record);

void main()
{
    while (1)   {
        /* ---- a report for each car in the input ----- */
        Automobile car = get_car();
        Trip_Record trip_totals = {{0,0,0},0,0,0,0};

        if (car.modelyear == 0)
            break;
        reportauto(car);
        /* -------- prepare the detail report ---------- */
        printf("\n  Date   miles  gas    cost    mpg  cost/mile");
        printf("\n-------- ----- ----- -------- ----- ---------");
        while (1)   {
            /* ----- get the next Trip_Record from the input ---- */
            Trip_Record trip = get_trip();
            if (trip.trip_date.mo == 0)
                break;
            /* -------- report the trip ----------- */
            reporttrip(trip);
            /* -------- collect totals for this car -------- */
            trip_totals.odometer_in =
                max(trip_totals.odometer_in, trip.odometer_in);
            trip_totals.odometer_out = trip_totals.odometer_out ?
                min(trip_totals.odometer_out, trip.odometer_out) :
                trip.odometer_out;
            trip_totals.gallons += trip.gallons;
            trip_totals.cost += trip.cost;
        }
        reporttrip(trip_totals);
    }
}

/* ---------- get a car's description from the input -------- */
static Automobile get_car(void)
{
    static Automobile car;

    car.modelyear = 0;
    scanf("%s", car.license);
    if (strcmp(car.license, "end") != 0)    {
        scanf("%d", &car.modelyear);
        scanf("%s", car.manufacturer);
    }
    return car;
}

static void reportauto(Automobile car)
{
    printf("\n\n%d %s License # %s\n",
        car.modelyear, car.manufacturer, car.license);
}

/* ------ get a trip record from the input stream ----- */
static Trip_Record get_trip(void)
{
    date dt;
    char strdate[9];
    odometer oin = 0, oout = 0;
    int gas = 0;
    Dollars cost = 0;
    static Trip_Record trip;

    dt.mo = 0;
    scanf("%s", strdate);
    if (strcmp(strdate, "end")) {
        dt.mo = atoi(strdate);
        dt.da = atoi(strdate + 3);
        dt.yr = atoi(strdate + 6);
        scanf("%ld", &oin);
        scanf("%ld", &oout);
        scanf("%d", &gas);
        scanf("%lf", &cost);
    }
    trip.trip_date = dt;
    trip.odometer_in = oin;
    trip.odometer_out = oout;
    trip.gallons = gas;
    trip.cost = cost;
    return trip;
}

static void reporttrip(Trip_Record trip)
{
    int miles = (int) (trip.odometer_in - trip.odometer_out);
    printf("\n%s %5d %5d %#8.2f %5d %#8.2f",
                trip.trip_date.mo ?
                    display_date(trip.trip_date) :
                    "totals  ",
                miles,
                trip.gallons,
                trip.cost,
                trip.gallons ? miles / trip.gallons : 0,
                miles ? trip.cost / miles : 0
                );
}

char *display_date(date dt)
{
    static char d[9];
    sprintf(d, "%2d-%02d-%02d", dt.mo, dt.da, dt.yr);
    return d;
}





[LISTING THREE]


// auto.hpp

// -----------------------------
// simple data element classes
// -----------------------------
typedef long odometer;
typedef double Dollars;

//  -----------------------------
// a simple date class
// -----------------------------
struct date {
    int mo, da, yr;
    char *display(void);
};

// -----------------------------
// Automobile class
// -----------------------------
class Automobile    {
private:
    int modelyear;
    char *manufacturer;
    char *license;
public:
    Automobile(char *license, int year, char *make);
    Automobile();
    ~Automobile();
    char *display(void);
    int nullcar(void) {return modelyear == 0;}
};

// -----------------------------
// Trip_Record class
// -----------------------------
class Trip_Record   {
private:
    date trip_date;
    odometer odometer_in;
    odometer odometer_out;
    int gallons;
    Dollars cost;
public:
    Trip_Record(void);
    Trip_Record(date, odometer, odometer, int, Dollars);
    int mileage(void) {return odometer_in - odometer_out;}
    char *display(void);
    int nulltrip(void) {return trip_date.mo == 0;}
    void operator += (Trip_Record&);
};





[LISTING FOUR]


// auto.cpp

#include <stream.hpp>
#include <string.h>
#include <stdlib.h>
#include "auto.hpp"

char *date::display()
{
    return form("%2d-%02d-%02d", mo, da, yr);
}

// --------------- constructors ---------------------------
Automobile::Automobile()
{
    modelyear = 0;
    manufacturer = license = NULL;
}

Automobile::Automobile(char *lic, int year, char *make)
{
    license = new char[strlen(lic)+1];
    strcpy(license, lic);
    modelyear = year;
    manufacturer = new char[strlen(make)+1];
    strcpy(manufacturer, make);
}

// -------------- destructor -------------------
Automobile::~Automobile()
{
    if (license != NULL)
        delete license;
    if (manufacturer != NULL)
        delete manufacturer;
}

char *Automobile::display()
{
    return form("%d %s License # %s",
                    modelyear, manufacturer, license);
}

#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))

void Trip_Record::operator += (Trip_Record& trip)
{
    this->odometer_in = max(this->odometer_in, trip.odometer_in);
    this->odometer_out = this->odometer_out ?
                         min(this->odometer_out, trip.odometer_out) :
                         trip.odometer_out;
    this->gallons += trip.gallons;
    this->cost += trip.cost;
}

// --------------- constructors ---------------------------
Trip_Record::Trip_Record()
{
    trip_date.mo = trip_date.da = trip_date.yr = 0;
    odometer_in = odometer_out = 0;
    gallons = 0;
    cost = 0;
}

Trip_Record::Trip_Record(date trdt, odometer oin, odometer oout,
                          int gas, Dollars cst)
{
    trip_date = trdt;
    odometer_in = oin;
    odometer_out = oout;
    gallons = gas;
    cost = cst;
}

char *Trip_Record::display()
{
    int miles = mileage();
    return form("%s %5d %5d %#8.2f %5d %#8.2f",
                trip_date.mo ? trip_date.display() : "totals  ",
                miles,
                gallons,
                cost,
                gallons ? miles / gallons : 0,
                miles ? cost / miles : 0
                );
}





[LISTING FIVE]


// autorpt.cpp

#include <stream.hpp>
#include <string.h>
#include <stdlib.h>
#include "auto.hpp"

// =========================================================
// automobile operating costs system
// =========================================================

// ------- prototypes
static Automobile& get_car(void);
static Trip_Record& get_trip(void);
static void report(Automobile&);
static void report(Trip_Record&);

main()
{
    while (1)   {
        // -------- a report for each car in the input
        Automobile car = get_car();
        if (car.nullcar())
            break;
        report(car);
        // -------- prepare the detail report
        cout << "\n  Date   miles  gas    cost    mpg  cost/mile";
        cout << "\n-------- ----- ----- -------- ----- ---------";
        // ---------- a Trip_Record to hold the totals
        Trip_Record trip_totals;

        while (1)   {
            // -------- get the next Trip_Record from the input
            Trip_Record trip = get_trip();
            if (trip.nulltrip())
                break;
            // -------- report the trip
            report(trip);
            // -------- collect totals for this car
            trip_totals += trip;
        }
        report(trip_totals);
    }
}

// ---------- get a car's description from the input
static Automobile& get_car(void)
{
    char license[7];
    int modelyear = 0;
    char make[25] = "";

    cin >> license;
    if (strcmp(license, "end") != 0)    {
        cin >> modelyear;
        cin >> make;
    }
    Automobile *car = new Automobile(license, modelyear, make);
    return *car;
}

static void report(Automobile& car)
{
    cout << "\n\n" << car.display() << '\n';
}

// ---------- get a trip record from the input stream
static Trip_Record& get_trip()
{
    date dt;
    dt.mo = 0;
    char strdate[9];
    odometer oin = 0, oout = 0;
    int gas = 0;
    Dollars cost = 0;

    cin >> strdate;
    if (strcmp(strdate, "end")) {
        dt.mo = atoi(strdate);
        dt.da = atoi(strdate + 3);
        dt.yr = atoi(strdate + 6);
        cin >> oin;
        cin >> oout;
        cin >> gas;
        cin >> cost;
    }
    Trip_Record *trip = new Trip_Record(dt,oin, oout, gas, cost);
    return *trip;
}

static void report(Trip_Record& trip)
{
    cout << '\n' << trip.display();
}