formatting using manipulators

16.3.2 Manipulators

As we could see, format flags are not particularly convenient. There is, however, a more „user friendly” mechanism of formatting data — the so called manipulators. These are basically functions defined in ios class and invoked by their names given as elements to be inserted to or extracted from a stream. As a result of their invocation, format flags can (although do not have to) be modified.
Actually, as we will see, manipulators do not have to be implemented as functions — they usually correspond to what is known as function objects, to be discussed in section on object functions .
There are tow kinds of manipulators: with and without parameters.


16.3.2.1 Parameterless manipulators

Parameterless manipulators are put into a stream without parentheses — just their names. There are quite a few such manipulators — their names correspond to the names of flags discussed in section on flags .
hexoctdec — set the base of integral numbers to be output, as does an invocation of the method setf(ios::hex,ios::basefield) etc. Modification of format flag is persistent, to change it, one has to use another manipulator or call setf again.
leftrightinternal — set the way data is justified within its field, as does the method setf(ios::left,ios::adjustfield) etc.
fixedscientific — they set the format for floating-point numbers, as does setf(ios::fixed,ios::floatfield) etc.
showbasenoshowbase — they act as invocation of the methods setf(ios::showbase) and setf(ios::noshowbase).
showpointnoshowpoint —  as invoking methods setf(ios::showpoint) or setf(ios::noshowpoint).
flush —  flushes output stream.
endl —  sends line termination character to output stream and flushes it, so all characters are immediately output to the destination of the stream (screen, file etc).
ends —  sends C-string terminator, ' \0', to output stream.
For example, the program 

P123: mani.cpp     Parameterless manipulators 

      1.  #include <iostream>
      2.  using namespace std;
      3.  
      4.  int main() {
      5.      int a = 0xdf, b = 0771, c = 123;
      6.  
      7.      cout << "dec (default): "
      8.           << dec << a << " " << b << " " << c << endl;
      9.  
     10.      cout << "hex, no showbase:   "
     11.           << hex << a << " " << b << " " << c << endl;
     12.  
     13.      cout.setf(ios::showbase);
     14.  
     15.      cout << "hex, with showbase: "
     16.           << a << " " << b << " " << c << endl;
     17.  
     18.      cout << "oct, with showbase: "
     19.           << oct << a << " " << b << " " << c << endl;
     20.  
     21.      cout.unsetf(ios::showbase);
     22.  
     23.      cout << "oct, no showbase:   "
     24.           << a << " " << b << " " << c << endl;
     25.  }

prints
    dec (default): 223 505 123
    hex, no showbase:   df 1f9 7b
    hex, with showbase: 0xdf 0x1f9 0x7b
    oct, with
We do not specify a base on line 15, as it has already been set as hex on line 11. We have to set it again only if we want to change it, e.g., for oct, as we do on line 19.

It is relatively easy to define our own parameterless manipulator. In order to do it, we have to define a function with one parameter of type 'reference to a stream' and returning by reference exactly the same stream; e.g.,
       ostream& my_manip(ostream& stream) {
           // ...
           return stream;
       }
When such a function is defined, we can use our manipulator just by inserting its name, without any arguments or parentheses, into a stream. The function will be invoked automatically with the current stream as an argument. It will return a reference to the stream, so the manipulator may be followed by another stream extraction or insertion operator, whichever is appropriate for the stream.
For example, the program 

P124: wmani.cpp     User defined manipulators 

      1.  #include <iostream>
      2.  using namespace std;
      3.  
      4.  ostream& scient(ostream&);
      5.  ostream& normal(ostream&);
      6.  ostream& acomma(ostream&);
      7.  
      8.  int main() {
      9.      double x = 123.456;
     10.      cout << scient << x << acomma
     11.           << normal << x << endl;
     12.  }
     13.  
     14.  ostream& scient(ostream& str) {
     15.      str.setf(ios::showpos | ios::showpoint);
     16.      str.setf(ios::scientific, ios::floatfield);
     17.      str.precision(12);
     18.      return str;
     19.  }
     20.  
     21.  ostream& normal(ostream& str) {
     22.      str.flags((ios::fmtflags)0);
     23.      return str;
     24.  }
     25.  
     26.  ostream& acomma(ostream& str) {
     27.      return str << ", ";
     28.  }

prints
    +1.234560000000e+02, 123.456
The first manipulator, scient, defined on lines 14-19, using methods already known to us, modifies format flag of the stream. The second,normal, restores default settings for the format flag; they correspond to the value of the format flag equal to zero of type ios::fmtflags — that is why we used casting on line 22. Finally, the third manipulator, acomma, does not modify any flags — it just inserts a comma and a space into the stream.
The parameter of the function defining a manipulator is in all cases of type ostream&. This makes the construct quite flexible: an argument does not have to be cout, it could be a reference to any object of type ostream or any type derived form ostream, e.g., an object of typeofstream representing an output stream connected to a file.


16.3.2.2 Manipulators with arguments

There are also manipulators with parameters. They are used in the same way as parameterless manipulators, but they require an argument (or arguments) to be specified (in parentheses, as in a function invocation). Usually, they are implemented as function objects (see section on function objects ).
Manipulators with parameters are accessible after including the header iomanip.
Several manipulators with parameters are already predefined; they perform the same tasks that the methods that we have already described, the main difference being the fact that they return, as all manipulators do, a reference to the stream they are inserted into.
setw(int wid) —  sets the field width for the next I/O operation, as the method width(int), that we have already described. The method returns old setting of the field width; the manipulator, as always, returns a reference to the stream. Both set the minimum value of field width — if this is not enough to represent a given piece of data, the field width will be expanded. Default value is 0, what means „as wide as needed, but no wider”.
setfill(int padd) —  sets padding character that is used to fill unused space when the field width is wider than the width of data to be output (space character by deault). Corresponds to the method fill.
setprecision(int prec) —  sets precision, as the method precision.
setiosflags(ios::fmtflags flag) —  modifies format flag, as one-argument method setf, i.e., „ORs” flag with the current format flag.
resetiosflags(ios::fmtflags flag) —  corresponds to the method unsetf;
setbase(int base) —  changes current value of base used when outputting integral numbers.
Some of these manipulators are demonstrated in the following program: 

P125: primatr.cpp     Formatting 

      1.  #include <iostream>
      2.  #include <iomanip>
      3.  using namespace std;
      4.  
      5.  void printMatrix(ostream&,double**,int,int,const char*);
      6.  
      7.  int main() {
      8.      const int DIM = 5;
      9.      double t[][DIM] = { {    1,  3,    5,       23, 16.42},
     10.                          {4.567,  4,    6,  234.345,    98},
     11.                          {  585, 34,    1,       67,  31.2},
     12.                          {    1,  0,    1, 2345.967, 123.2},
     13.                          {  1.2, 10, 34.1,    5.900,   0.2}
     14.                        };
     15.      double* tab[DIM];
     16.      for (int i = 0; i < 5; i++) tab[i] = t[i];
     17.  
     18.      char  name[5];
     19.      int  prec = 3;
     20.  
     21.      cout << "Name: ";
     22.      cin  >> setw(5) >> name;
     23.  
     24.      printMatrix(cout, tab, DIM, prec, name);
     25.  }
     26.  
     27.  void printMatrix(ostream& strm, double* tab[], int size,
     28.                               int prec, const char* name) {
     29.      ios::fmtflags old =
     30.          strm.setf(ios::fixed, ios::floatfield);
     31.  
     32.      strm << setiosflags(strm.flags() | ios::showpoint)
     33.           << setprecision(prec) << "\nMatrix: " << name
     34.           << "\n\n";
     35.  
     36.      for (int i = 0; i < size; i++) {
     37.          strm << "ROW " << setfill('0') << setw(2)
     38.               << (i+1)  <<  ":" << setfill(' ');
     39.          for (int j = 0; j < size; j++)
     40.              strm << setw(9) << tab[i][j];
     41.          strm << endl;
     42.      }
     43.      strm << endl << setiosflags(old);
     44.  }

The program defines a function which prints a matrix in a readable form (the matrix is passed as an array of pointers to the beginnings of rows). Note that a stream to which the matrix is to be output is passed as an argument — in this way the same function can be used to output the matrix to a file.
Note also the way the program reads a name from the user (line 22). As the array which stores the name is only 5 characters long, we usesetw to limit the length of a C-string which can be read: even if the user enters a longer name, it will be trucated (to four characters + NUL), but no overflow of the array will occur:
  Name: zanzibar

  Matrix: zanz

  ROW 01:    1.000    3.000    5.000   23.000   16.420
  ROW 02:    4.567    4.000    6.000  234.345   98.000
  ROW 03:  585.000   34.000    1.000   67.000   31.200
  ROW 04:    1.000    0.000    1.000 2345.967  123.200
  ROW 05:    1.200   10.000   34.100    5.900    0.200
Another important issue is avoiding side effects when calling a function. That is why our function, before returning, restores the format flag to the state it was on entry (lines 29 and 43).
It is possible to define our own manipulators with parameters, but it is more complicated than it was for parameterless manipulators; we will come back to this issue in section on function objects .

No comments:

Post a Comment