In class yesterday I made a number of modifications to the mpg.cpp program that was presented in the previous lecture notes. The changes I made will help you with the lab assignment and include:

  1. The addition of file I/O
  2. The addition of command line arguments
  3. The addition of the exit function

Also note that I changed the way I handle input in the while loop. The way I did it previously, by putting the read inside the while condition, is a bit sloppy, because file I/O can fail for reasons other than end-of-file. In these other cases, we may want to skip the line of input and continue.

I have now placed a priming read before the while loop and then used the eof method to determine whether the end-of-file has been reached. The priming read initializes the input variables and the file's eof flag. If you are familiar with the phrase "priming the pump", this is the phrase from which priming read is derived. If I fail to put the priming read before the loop, then the loop will get executed once, even if the input file is empty. In that case garbage will get printed because nothing will be read into the input variables.

I have also placed a read statement at the end of the loop to get the next line of input.


The bold-faced lines are the ones that differ from the mpg program in the previous lecture notes.
#include <iostream>
#include <string>
#include <fstream> -- add fstream to enable file I/O
#include <stdlib.h> -- stdlib.h declares the exit function

using namespace std;

// auto turns out to be a C++ reserved word so I substituted car instead
class car {
public:
  string name;
  float avg_mpg;
};

// it's better to declare constants rather than hard-coding them
const int NUM_DRIVERS = 3;


/* in order to use command line arguments you must declare the following
   two arguments for main:

   1) argc: the number of command line arguments. It is equal to 1 plus the
       number of command line arguments you provide, because the first 
       command line argument is actually the name of the executable
   2) argv: an array of character strings that contain the command line
       arguments. argv[0] is the name of the executable. The command line
       arguments provided by the user start at argv[1].
*/
main(int argc, char *argv[]) {

  ifstream input_file; -- input_file is an (i)nput (f)ile stream
  car max_car;  // holds the car with the best gas mileage seen thus far
  float avg;    // average gas mileage for the current car
  int mpg[NUM_DRIVERS];  // gas mileage achieved by drivers for current car
  string model;   // name of current car


  /* make sure that we receive exactly the number of arguments that we
     expect. If we receive too few, then argv[1] will be undefined and our
     program will crash. If we receive too many, the extra arguments will
     be ignored. More importantly, the user probably has a misconception
     about how to use our program.
  */
  if (argc != 2) {
    cerr << "usage: " << argv[0] << " filename" << endl;
    /* exit provides us with a way to immediately exit the program and to
       provide the user with an error code that indicates why the program
       exited abnormally. There are no "standard" values for error codes. They
       are defined by the programmer. */
    exit(1);  
  }

  /* we must now open the input file and then check to ensure that it was
     opened correctly. We can test the stream directly, as I have done here,
     or you can call the stream's fail method. My method is the more commonly
     accepted idiom. If the open fails, it writes an error message to a
     protected part of memory. perror, which stands for (p)rint error, is
     a C++ provided function that can read this protected error message and
     print it to the console. You can also provide an argument to perror, in
     which case perror concatenates your argument to the start of the error
     message. Providing an argument to perror can help you locate where in the
     source file the error has occurred.
  */
  input_file.open(argv[1]);
  if (!input_file) {
    perror(argv[1]);
    exit(1);
  }

  // initialize max_car.avg_mpg to a small value or else it might never get set
  max_car.avg_mpg = -1.0; 


    /* initial priming read that initializes the input variables and sets
       the eof flag to true if the file is empty */
    input_file >> model >> mpg[0] >> mpg[1] >> mpg[2];


  /* the eof method returns true when end-of-file is reached */

  while (input_file.eof() != true) {

    // remember to cast either the numerator or divisor to a float in order
    // to avoid integer arithmetic
    avg = (mpg[0] + mpg[1] + mpg[2]) / (float)NUM_DRIVERS;
    if (avg > max_car.avg_mpg) {
      max_car.name = model;
      max_car.avg_mpg = avg;
    }

    /* read the next line of input */
    input_file >> model >> mpg[0] >> mpg[1] >> mpg[2];

  }
  cout < "best car = "
       < max_car.name
       < endl;
  cout < "mpg = " 
       < max_car.avg_mpg
       < endl;
}