### Defining Your Own Format State Flags

If you have defined your own input or output operator for a class type, you may want to offer some flexibility in how you handle input or output of instances of that class. The I/O Stream Classes let you define stream-specific flags that you can then use with the format state member functions such as setf() and unsetf(). You can then code checks for these flags in the input and output operators you write for your class types, and determine how to handle input and output according to the settings of those flags.

For example, suppose you develop a program that processes customer names and addresses. In the original program, the postal code for each customer is written to the output file before the country name. However, because of postal regulations, you are instructed to change the record order so that the postal code appears after the country name. The following example shows a program that translates from the old file format to the new file format, or from the new file format to the old.

The program checks the input file for an exclamation mark as the first byte. If one is found, the input file is in the new format, and the output file should be in the old format. Otherwise the reverse is true. Once the program knows which file should be in which format, it requests a free flag from each file's stream object. It reads in and writes out each record, and closes the file. The input and output operators for the class check the format state for the defined flag, and order their output accordingly.

    // Defining your own format flags

    #include <fstream.h>
    #include <stdlib.h>

    long InFileFormat=0;
    long OutFileFormat=0;

    class CustRecord {
       public:
          int Number;
          char Name[48];
          char Phone[16];
          char Street[128];
          char City[64];
          char Country[64];
          char PostCode[10];
       };

    ostream& operator<<(ostream &os, CustRecord &cust) {
       os << cust.Number << '\n'
          << cust.Name << '\n'
          << cust.Phone << '\n'
          << cust.Street << '\n'
          << cust.City << '\n';
       if (os.flags() & OutFileFormat)    // New file format
          os << cust.Country << '\n'
             << cust.PostCode << endl;
       else                               // Old file format
          os << cust.PostCode << '\n'
             << cust.Country << endl;
       return os;
       }

    istream& operator>>(istream &is, CustRecord &cust) {
       is >> cust.Number;
       is.ignore(1000,'\n'); // Ignore anything up to and including new line
       is.getline(cust.Name,48);
       is.getline(cust.Phone,16);
       is.getline(cust.Street,128);
       is.getline(cust.City,64);
       if (is.flags() & InFileFormat) {   // New file format!
          is.getline(cust.Country,64);
          is.getline(cust.PostCode,10);
          }
       else {
          is.getline(cust.PostCode,10);
          is.getline(cust.Country,64);
          }
       return is;
       }

    void main(int argc, char* argv[]) {
       if (argc!=3) {                        // Requires two parameters
          cerr << "Specify an input file and an output file\n";
          exit(1);
          }
       ifstream InFile(argv[1]);
       ofstream OutFile(argv[2],ios::out);

       InFileFormat = InFile.bitalloc();     // Allocate flags for
       OutFileFormat = OutFile.bitalloc();   // each fstream

       if (InFileFormat==0 ||                // Exit if no flag could
           OutFileFormat==0) {               // be allocated
          cerr << "Could not allocate a user-defined format flag.\n";
          exit(2);
          }

       if (InFile.peek()=='!') {             // '!' means new format
          InFile.setf(InFileFormat);         // Input file is in new format
          OutFile.unsetf(OutFileFormat);     // Output file is in old format
          InFile.get();                      // Clear that first byte
          }
       else {                                // Otherwise, write '!' to
          OutFile << '!';                    // the output file, set the
          OutFile.setf(OutFileFormat);       // output stream's flag, and
          InFile.unsetf(InFileFormat);       // clear the input stream's
          }                                  // flag

       CustRecord record;
       while (InFile.peek()!=EOF) {         // Now read the input file
          InFile >> record;                 // records and write them
          OutFile << record;                // to the output file,
          }

       InFile.close();                     // Close both files
       OutFile.close(); }

The following shows sample input and output for the program. If you take the output from one run of the program and use it as input in a subsequent run, the output from the later run is the same as the input from the preceding one.

Input File | Output File
--- | ---
    3848
    John Smith
    4163341234
    35 Baby Point Road
    Toronto
    M6S 2G2
    Canada
    1255
    Jean Martin
    0418375882
    48 bis Ave. du Belloy
    Le Vesinet
    78110
    France
    !3848
    John Smith
    4163341234
    35 Baby Point Road
    Toronto
    Canada
    M6S 2G2
    1255
    Jean Martin
    0418375882
    48 bis Ave. du Belloy
    Le Vesinet
    France
    78110

Note that, in this example, a simpler implementation could have been to define a global variable that describes the desired form of output. The problem with such an approach is that later on, if the program is enhanced to support input from or output to a number of different streams simultaneously, all output streams would have to be in the same state (as far as the user-defined format variable is concerned), and all input streams would have to be in the same state. By making the user-defined format flag part of the format state of a stream, you allow formatting to be determined on a stream-by-stream basis.