The C++ I/O System

  • I/O Class Hierarchy (simplified)
                ios_base
                   |
                  ios
                /     \
          istream      ostream
           |    \      /   |
           |    iostream   |
           |       |       |
    ifstream    fstream    ofstream
    
  • The class ios_base -- public variables and methods
  • The derived classes istream, ostream
  • , ifstream, ofstream
  • Overloads of operators << and >>
  • Manipulators

The C++ I/O System

The C++ I/O system defined in the IOStreams library is quite complex, extraordinarily useful and flexible, and quite beautiful in design. The current system resides in the namespace std, but is the result of at least a decade of evolution fuelled by theory, experiment, and the ISO standardization process.

A comprehensive treatment of C++ IOStreams is well beyond the scope of these notes. In fact there is a very good 640 page treatise on the subject that is recommended for further study (see [Standard C++ IOStreams and Locales - Advanced Programmers Guide and Reference, by Angelika Langer and Klaus Kreft, Addison Wesley, 2000]). What is intended here is a detailed introduction to the most useful instantiations in this library, sufficient for most programming course work. For professional levels of expertise, you are strongly encouraged to obtain the Langer & Kreft reference and keep it alongside your Stroustrupp.

A portion of the iostreams class hierarchy is shown in the slide. This is the portion that we will discuss in some detail. A more complete hierarchy is as follows:

                                 ios_base
                                    |
                           basic_ios<charT, traits>
                           /                      \
basic_istream<charT, traits>                      basic_ostream<charT, traits>
       |                   \                      /            |
       |                 basic_iostream<charT, traits>         |
       |                              |                        |
basic_ifstream<charT, traits>         |           basic_ofstream<charT, traits>
                                      |
                          basic_fstream<charT, traits>

Notice that all of the classes except the base class ios_base are templates. The two template parameters represent the character type being used along with properties of that type. There are two predefined character types, the familier 1-byte ascii characters char and the 4-byte wide characters wchar_t. The type char supports ASCII, EBCDIC, and ISO 8859-2 character sets. The type wchar_t supports Unicode and ISO 10646. The user can invent other character classes and associated traits and instantiate streams using that type.

Even the diagram above does not illustrate the full generality of the IOStream library. There is support for internationalization in the form of Locales, and there is a hierarchy of buffer types that are used by iostreams. These are interesting and important features that are beyond the scope of these notes.

There is also support for exception handling built into the class hierarchy shown above, but we will not say much about that aspect either. Because we will assume the standard ASCII character set, there is no need to go into detail on the traits classes in these notes. Finally, there is a parallel set of classes for I/O into strings instead of streams. To summarize, here is a list of advanced features of the IOStream library that we will not discuss in this chapter:

For complete details on all these features of C++ IOStreams, see the Langer and Kreft reference.

For most purposes, it suffices to use the ASCII character set and stream I/O, and there are type definitions in the standard library associated with those assumptions:

typedef basic_ios      < char, char_traits < char > >  ios;
typedef basic_istream  < char, char_traits < char > >  istream;
typedef basic_ostream  < char, char_traits < char > >  ostream;
typedef basic_iostream < char, char_traits < char > >  iostream;
typedef basic_ifstream < char, char_traits < char > >  ifstream;
typedef basic_ofstream < char, char_traits < char > >  ofstream;
typedef basic_fstream  < char, char_traits < char > >  fstream;

Note that making these substitutions yields the hierarchy illustrated in the slide.

In the remainder of this chapter we will discuss details of the classes ios_base (equivalently class ios), the derived classes istream, ostream, ifstream, ofstream, and the collection of IO manipulators defined with these classes.

Class ios_base

namespace std
{
  class ios_base 
  {
  public:
    // ios_base status methods
    // these read and/or manipulate the status bits
    bool  fail () const; // true iff bad or fail state 
    bool  eof  () const; // true iff stream is at end of file
    ...
    // ios_base binding methods
    ostream*  tie (ostream*);   // ties current stream to specified ostream
    ...
    // ios_base formatting methods
    format_flags setf (format_flags flags);
    ...
    // ios_base data methods
    int  width(int val);
    char fill (char fillch);
    ...
    // ios_base enumerated bitmask types
    enum iostate     { ... }; // 4 io states, representing good, eof, bad, fail
    enum formatflags { ... }; // 15 format flags, such as left, showpoint, hex
    enum openmode    { ... }; // 6 file open modes, such as in, out, binary
    enum seekdir     { ... }; // 3 file seek modes: beg, cur, end

  protected:
    unsigned long state;          // stores status bits
    unsigned long flags;          // stores flag bits
    unsigned long mode;           // stores mode bits
    int           widthvalue,     // initialized to 0
                  precisionvalue; // initialized to 0
    char          fillcharacter;  // initialized to ' '
    streambuff*   streambuffer;   // pointer to a streambuff object

    // plus other data, such as specifying tied streams
  }  ;  // end pseudo-definition of class ios_base
} // namespaced std

Class ios_base

I/O streams can be understood by exploring in detail the the class ios_base, which contains all of the public member variables and most public methods in the hierarchy. This slide shows the basic organization of the class. We explore the specifics in more detail in the following slides.

Items to take note of at this point are:

Understanding how streams work is greatly facilitated by following an instantiation of the base class ios_base from which all of the stream classes derive. A key to this understanding is the use of the enumerated types, which we discuss individually in the next slides.

ios_base Status Methods

class ios_base
{
public:
  ...
  bool  good           () const; // true iff no error flag is set
  bool  eof            () const; // true iff eofbit is set
  bool  fail           () const; // true iff badbit or failbit are set
  bool  bad            () const; // true iff badbit is set
  bool  operator !     () const; // same as fail()
        operator void* () const; // null pointer if fail(), non-null otherwise
  void  clear          (iostate newState = goodbit); // sets state to newState
  void  setstate       (iostate addState); // adds addState to existing state,
                       // i.e., the new state is oldStreamState | addState
  ...
  enum iostate // io states
    {
      goodbit = 0x0000, // everything is OK; not really a bit, but required to be 0
      eofbit  = 0x0001, // stream is at end of file
      failbit = 0x0002, // the last I/O operation failed, otherwise OK
      badbit  = 0x0004  // a serious error has occurred, stream unusable
    } ;
  ...
protected:
  ...
  unsigned long state;  // stores status bits
  ...

ios_base Status Methods

This slide shows one possible setup for the iostate enumerated type and the public member functions that use it. Some useful code techniques can be illuminated with this information. For example:

std::ifstream in1;
...
in1.open(filename);
while (!in1)
{ 
  std::cout << "Cannot open file " << filename << " - try again: ";
  std::cin >> filename;
  in1.clear();
  in1.open(filename);
}
...

uses the ! operator to detect a problem opening a file. This operator is defined for std::ifstream objects because std::ifstream is a public derived class of ios.

You may also note that the interface allows client manipulation of the io states through both clear(state) and setstate(state). These differ in effect, as can be understood best by looking at the implementations:

void std::ios::clear (iostate newState) // default argument is goodbit
{
  state = newState;
} 

void std::ios::setstate (iostate addState)
{
  state = state | addState; // bitwise operation - set the designated bit
} 

The implementation of setstate uses bitwise operations. These are native operations of C that allow direct access to integral values at the bit level. Most current CPUs have hardware support for bitwise operations, making them extremely efficient, typically requiring one clock cycle to accomplish the entire operation. We will return to bitwise operations in a later chapter. Meantime, here is a table showing the bitwise operations and their semantics:

 operation     symbol   type  infix version  accumulator version    
  and   &  binary  z = x & y  z &= y
  or   |  binary  z = x | y  z |= y
  xor   ^  binary  z = x ^ y  z ^= y
  not   ~  unary  z = ~y  (na)
  left shift   <<  binary  z = y << n  (na)
  right shift       >>  binary      z = y >> n      (na)

The line of code implementing setstate(addState) would be equivalent to this bitwise arithmetic, if we had addState = 00000010 and state = 00001000 and

state = state | addState
      = 00001000 | 00000010
      = 00001010


ios_base Formatting Methods

class ios_base
{
public:
  ...
  fmtflags flags () const;  
    // returns current flags (interpret using enumerated type)
  fmtflags flags (fmtflags newflags);
    // sets flags to newflags; returns oldflags
  fmtflags setf  (fmtflags setbits);
    // sets those flags specified in setbits; returns oldflags
    // called by iomanipulator setiosflags
  fmtflags setf  (fmtflags setbits, fmtflags mask);
    // unsets flags in mask, then sets flags in setbits & mask
    // called by iomanipulator resetiosflags
  fmtflags unsetf (fmtflags unsetbits);
    // clears flags specified in unsetbits
  ...
  enum fmtflags
    // parameter for ios methods setf(), unsetf(), e.g.,
    // setf(ios::right | ios::dec | ios::showpoint | ios::fixed)
    {
      skipws , left , right , internal , 
      dec , oct , hex , showbase , showpoint , showpos , scientific , fixed ,
      uppercase ,  unitbuf , boolalpha ,
      adjustfield , basefield , floatfield
    };
  ...
protected:
  ...
  unsigned long flags;  // stores flag bits
  ...
};

ios_base Formatting Methods


Meaning of Format Flags

enum  fmtflags
  // parameter for ios_base methods setf(), unsetf(), e.g.,
  // setf(ios_base::right | ios_base::fixed)
  {
    boolalpha  = 0x0001, // reads and writes bool values in alphabetic
    left       = 0x0002, // left-justify output
    right      = 0x0004, // right-justify output
    internal   = 0x0008, // (numeric output) prefix left..fill..number right
    skipws     = 0x0010, // skip white space before extraction

    dec        = 0x0020, // decimal 
    hex        = 0x0040, // hexadecimal 
    oct        = 0x0080, // octal

    // dec, oct, hex affect both input and output. On input, integral values
    // are interpreted in base notation and output uses appropirate mode.
    // Default is no bits set and means "C rules" apply:
    //    output is decimal output and input of integral types is 
    //    interpreted  as dec, oct, or hex depending leading/prefix:
    //    (leading 1..9) => dec, (leading 0) => oct, (leading 0x) => hex

    showbase   = 0x0100, // show base indicator on output (0 and 0x)
    showpoint  = 0x0200, // show decimal point (for fixed point output)
    showpos    = 0x0400, // out: force show of sign for positive numbers

    fixed      = 0x0800, // out: force decimal notation for float
    scientific = 0x1000, // out: force scientific notation for float

    unitbuf    = 0x2000, // out: flush buffer after each insertion
    uppercase  = 0x4000, // out: use upper case indicators for hex and dec 

    // convenience definitions:
    adjustfield = left | right | internal;
    basefield = dec | hex | oct;
    floatfield = scientific | fixed;
  };

Meaning of Format Flags


ios_base Data Methods

class ios_base
{
public:
  ...
  char fill (char fillch);
  // sets fill character (default = ' '); returns old fill_character;
  // called by iomanipulator setfill (char)

  int precision () const;  // returns precision_value (>= 0)
  int precision (int val);
  // sets precision (default = 0, which means as precise as possible);
  // called by iomanipulator setprecision (int).

  int width () const;       // returns width value (>= 0)
  int width (int val);
  // sets width_value; note: width reset to 0 after each extraction
  // called by iomanipulator setw (int)
  ...
protected:
  ...
  int          width_value, precision_value; // initialized to 0
  char         fill_character;               // initialized to ' '
  ...
};

ios_base Data Methods


Meaning of Width and Precision

  // meaning of width (n)
  // n <= 0: no effect
  // n >  0: on output, sets minimum number of characters output 
  //           (filled with fill char)
  //         on input, sets length of buffer for string extractions
  // Note: reset to 0 after each insertion and extraction
  // Called by manipulator setw (n)

  // meaning of precision (n)
  // n <= 0: default
  // n >  0: if ios::fixed bit is set, determines number of places
  //            displayed after decimal point (forced if ios::showpoint
  //            is also set)
  //         if ios::fixed is not set, determines number of significant
  //            digits displayed
  // Called by manipulator setprecision (n)

Meaning of Width and Precision


ios_base Binding Methods

class ios_base
{
public:
  ...
  streambuf* rdbuf          (); // returns ptr to stream's streambuff object
  ostream*   tie            (); // returns ptr to the tied ostream
  ostream*   tie            (ostream*); 
  // ties current stream to specified ostream, which is called the "tied
  // stream".  Returns pointer to previously tied stream. 
  // For an istream ist, this means that the tied stream will be
  // flushed whenever the ist needs input; for an ostream ost, this means that
  // the tied stream will be flushed whenever ost is flushed.
  // By default, cin is tied to cout.

  static bool sync_with_stdio (bool sync = true);
  // true causes cin, cerr, clog, cout to operate by means of the C standard I/O
  // connections stdin, stdout, and stderr; also implies unit buffering.
  // Returns previous value.
  // This is useful when mixing old C code with C++, otherwise not recommended.
  ...
protected:
  ...
  streambuff*  streambuffer;   // pointer to a streambuff object
  ostream*     tied_ostream;   // pointer to an ostream object
  ...
};

ios_base Binding Methods


ios_base File Modes

class ios_base
{
public:
  ...
  enum open_mode // open modes
    // optional second parameter to the fstream open() method, e.g.,
    // open(filename, ios_base::out | ios_base::binary)
    {
      in         = 0x0001,  // open file for input -- read
      out        = 0x0002,  // open file for output -- write
      ate        = 0x0004,  // seek to eof when file is opened 
                            // -- "automatic to end"
      app        = 0x0008,  // open file in append mode 
                            // -- writing will occur at end
      trunc      = 0x0010,  // truncate the file if it exists
      binary     = 0x0020   // open file in binary mode 
                            // -- write bytes only, no formatting characters
    };

  enum seek_dir // file seek modes
    {
      beg        = 0x0100,  // seek relative to beginning of file
      cur        = 0x0200,  // seek relative to current position
      end        = 0x0400   // seek relative to end of file
    };
  ...

protected:
  ...
  unsigned long mode;  // stores open and seek mode bits
  ...
};

ios_base File Modes


Meaning of File Modes

Open Mode Effect Effect with ...|ate
in Opens text files for reading, initial position at beginning of file Initial position is at end of file
out
out|trunc
Truncates file to empty, or creates file, for write only No effect on empty file
out|app Appends; opens or creates text file for writing at end of file No additional effect
in|out Opens file for update (read or write), with position at beginning of file Position is at end of file
in|out|trunc     Opens file for update, truncates to empty No effect on empty file

With the binary flag added (...|binary), all conversions are suppressed.
This is like a binary data copy.

Meaning of File Modes


Possible ios_base Method Implementations

ios_base::fmtflags ios_base::flags () const
{
  return flags;
}

ios_base::fmtflags ios_base::flags (ios_base::fmtflags newflags)
{
  ios_base::fmtflags oldflags = flags;
  flags = newflags;
  return oldflags;
}

ios_base::fmtflags ios_base::setf (ios_base::fmtflags setbits)
{
  ios_base::fmtflags oldflags = flags;
  flags |= setbits;
  return oldflags;
}

ios_base::fmtflags ios_base::setf (ios_base::fmtflags setbits, ios_base::fmtflags mask)
{
  ios_base::fmtflags oldflags = flags;
  flags = (flags & ~mask) | (setbits & mask);
  // unsetf(mask); setf(setbits & mask);
  return oldflags;
}

ios_base::fmtflags ios_base::unsetf (ios_base::fmtflags unsetbits)
{
  ios_base::fmtflags oldflags = flags;
  flags &= ~unsetbits;
  return oldflags;
}

char ios_base::fill (char newfill) 
{
  char oldfill = fill_character;
  fill_character = newfill;
  return oldfill;
}

int ios_base::precision () const
{
  return precision_value;
}

int ios_base::precision (int val);
{
  int oldprecision = precision_value;
  precision_value = val;
  return oldprecision;
}

int ios_base::width () const
{
  return width_value;
}

int ios_base::width (int val)
{
  int oldwidth = width_value;
  width_value = (val);
  return oldwidth;
}

Possible ios_base Method Implementations


Class istream

namespace std
{
  class istream : public class ios_base
  {
    // overloads of input operator
    friend istream& operator >> (istream& , char);
    friend istream& operator >> (istream& , int);
    friend istream& operator >> (istream& , long);
    friend istream& operator >> (istream& , unsigned char);
    friend istream& operator >> (istream& , unsigned int);
    friend istream& operator >> (istream& , unsigned long);
    friend istream& operator >> (istream& , float);
    friend istream& operator >> (istream& , double);
    friend istream& operator >> (istream& , long double);
    friend istream& operator >> (istream& , char* );
    ...
    public:
      // methods to access next element in stream
      char get ();  
      void get (char&);
      char peek ();
      ...
  } cin;  // predefined object
} // namespace std

Class istream


Class ostream

namespace std
{
  class ostream : public class ios_base
  {
    // overloads of output operator
    friend ostream& operator << (ostream& , char);
    friend ostream& operator << (ostream& , int);
    friend ostream& operator << (ostream& , long);
    friend ostream& operator << (ostream& , unsigned char);
    friend ostream& operator << (ostream& , unsigned int);
    friend ostream& operator << (ostream& , unsigned long);
    friend ostream& operator << (ostream& , float);
    friend ostream& operator << (ostream& , double);
    friend ostream& operator << (ostream& , long double);
    friend ostream& operator << (ostream& , const char* );
    ...
  public:
    // method to send element to stream
    void put(char ch);
    ...
  } cout, cerr, clog;  // predefined objects
} // namespace std

Class ostream


Predefined Objects cin, cout, cerr, clog

  • Predefined istream object: cin
  • Predifined ostream objects: cout, cerr, clog
  • Buffered ostreams: cout, clog
  • Tied stream pair: cin, cout
  • cin.tie(&cout); // in file iostream
    

Predefined Objects cin, cout, cerr, clog


Class ifstream

class ifstream : public istream
{
public:
  ...
  ifstream* open
    (
      const char* filename, 
      ios_base::open_mode mode = ios_base::in
    );
    // opens file in specified mode, sets fail bit on failure
    // returns this on success, NULL on failure 

  ifstream* close ();
    // closes file previously opened with open()
    // fails immediately if no file is currently open
    // sets fail bit if any operation fails
    // returns this on success, NULL on failure
  ...
};

Class ifstream


Class ofstream

class ofstream : public ostream
{
public:
  ...
  ofstream* open
    (
      const char* filename,
      ios_base::open_mode mode = ios_base::out | ios_base::trunc
    );
    // opens file in specified mode, sets fail bit on failure
    // returns this on success, NULL on failure 

  ofstream* close ();
    // flushes buffer, then closes file previously opened with open()
    // fails immediately if no file is currently open
    // sets fail bit if any operation fails
    // returns this on success, NULL on failure
  ...
};

Class ofstream


I/O Manipulators - 1

// these take no parameters, defined in <iostream>
boolalpha;       // calls s.setf(ios::boolalpha)
noboolalpha;     // calls s.unsetf(ios::boolalpha)
showbase;        // calls s.setf(ios::showbase)
noshowbase;      // calls s.unsetf(ios::showbase)
showpoint;       // calls s.setf(ios::showpoint)
noshowpoint;     // calls s.unsetf(ios::showpoint)
showpos;         // calls s.setf(ios::showpos)
noshowpos;       // calls s.unsetf(ios::showpos)
uppercase;       // calls s.setf(ios::uppercase)
nouppercase;     // calls s.unsetf(ios::uppercase)
skipws;          // calls s.setf(ios::skipws)
noskipws;        // calls s.unsetf(ios::skipws)
unitbuf;         // calls s.setf(ios::unitbuf)
nounitbuf;       // calls s.unsetf(ios::unitbuf)
left;            // calls s.setf(ios::left, ios::adjustfield)
right;           // calls s.setf(ios::right, ios::adjustfield)
internal;        // calls s.setf(ios::internal, ios::adjustfield)
dec;             // calls setf(ios::dec, ios::basefield)
hex;             // calls setf(ios::hex, ios::basefield)
oct;             // calls setf(ios::oct, ios::basefield)
fixed;           // calls setf(ios::fixed, ios::floatfield)
scientific;      // calls setf(ios::scientific, ios::floatfield)
endl;    // flushes streambuff and inserts '\n'
ends;    // flushes streambuff and inserts '\0'
flush;   // flushes streambuff

I/O Manipulators - 1


I/O Manipulators - 2

  • Prototype for 0-parameter manipulators:
    stream_type& manip_name (stream_type&);
    
  • User can define and use manipulators, e.g.:
    // define:                               // use:
    ostream& beepbeep (ostream& os)          std::cout << beepbeep << ...
    {
      os << "\a\a";
      return os;
    }
    

I/O Manipulators - 2


I/O Manipulators - 3

  • These manipulators take parameters and are more complicated to define
  • the implementations use templates and function objects
  • defined in <iomanip>
  • setbase       (int b);      // sets base (radix) for numericals
    // b = 8, 10, 16 sets oct, dec, hex flag, respectively
    // b = 0 means default: decimal on output, C rules for integers on input
    setiosflags   (ios::format_flags mask); // calls ios::setf (mask)
    resetiosflags (ios::format_flags mask); // calls ios::unsetf (mask)
    setfill       (char ch);    // calls ios::fill (ch)
    setprecision  (int n);      // calls ios::precision (n)
    setw          (int n);      // calls ios::width (n)
    
  • User may define a more extensive collection

I/O Manipulators - 3