GO
home account info subscribe login search FAQ/help site map contact us


 
Brief Full
 Advanced
      Search
 Search Tips
[an error occurred while processing this directive]
Previous Table of Contents Next


Steps

1.  Determine the operations necessary on the stream to get the data into or out of the stream in the way you want.
2.  Create a function taking a stream (input or output, depending on the operation) as a reference argument, and returning one.
ostream& setfmt( ostream& output )
3.  In that function, perform all the modifications that need to be done—that is, setting widths, precision, numerical base, and adding any characters or other data to the stream.
ostream& setfmt( ostream& output )
{
     output << setw( 9 ) ;
     output.setf( ios:: right ) ;

   return output ;
}
4.  Create another function to undo the modifications to the stream. This is not necessary if all that happened was that the manipulator inserted some formatting characters into the stream (tabs, dollar signs, or ANSI control sequences).
ostream& unsetfmt( ostream& output )
{
     output << setw( 0 ) ;
     output.unsetf( ios:: right ) ;
     output << “ degrees.” ;

   return output ;
}
5.  Use the manipulator and the unmanipulator in your code:
os << setfmt << write.val() << unsetfmt ;

How It Works

Stream operations get their relatively easy-to-learn syntax from the fact that they return stream objects themselves (whose functions can be called, and so on). Manipulators use this syntax too, and that makes it possible to easily hook in in-stream formatting style manipulators when convenient.

The advantages of this technique are

  Reduced chance of formatting errors because some stream manipulator was left out or was not unset.
  Easier-to-understand syntax to the reader or maintainer of the program, provided the manipulators are aptly named.
  More consistent formatting—to change the input or output style of the data, only one function needs to be changed.

The following code listing, usrmanip.cpp, shows how to use non-parameterized manipulators:

// File: usrmanip.cpp
// Example for C++ How-To
// Writing your own stream manipulators
// Copyright 1998, Jan Walter
// NO WARRANTY. If this code breaks you get to keep both pieces.

// Compiler verificiation:
// Borland C++ 5.01A:                Yes
// DJGPP 2.01 w. GCC 2.81 :      Yes
// Watcom 10.5:                         Not Tried
// Microsoft VC++ 5:                    Not Tried
// GCC 2.7.2/Linux:                    Not tried
// GCC/EGCS/Linux:                    Not tried

// This sample program details how to write your own streams manipulators.
// While it is only possible to write manipulators that don’t take parameters
// without modifying the actual streams source code (which is beyond the
// scope of this book), the advantages of having only one function to set
// a stream to the way it’s needed (and another to return it to its original
// state) are many: it’s easier, more clearer for others to understand, and
// less likely to result in program bugs or undesired behavior.

// In this example we will use non-parameterized IO manipulators to set the
// width of numerical output and the alignment of numerical output in one
// operation. This technique can be combined with overloaded insertion and
// extraction operators to make code more readable and less susceptible to
// oversights (i.e. bugs and side effects).

// What the program itself does:
// This program reads standard input for numbers as fractions, numerator and
// then denominator, interpreted as fractions of the angle of full circle
// (i.e. 360 degrees) and converts those to the angle in degrees. For example,
// 25 100 (meaning the fraction of 25 over 100) would be 90 degrees.
//
// The best way to feed data to a program of this type is to put all of the
// numbers into a text file, and then redirect the file into the program’s
// standard input using the operating system shell’s redirection symbol.
// i.e. usrmanip < angles.txt
//
//     If you choose to enter fractions manually, the letter Ctrl-Z is the
// end-of-file character and will denote to the program that you’re finished
// entering data.

#include <iostream.h>
#include <iomanip.h>

#ifdef __BORLANDC__
#pragma hdrstop
#endif

#include <string>

// The program will define 2 manipulators - one that configures the stream
// to the way we want it for output, and the other one to append some text and
// set it back to the way it was.

ostream& setfmt( ostream& output )
{
     output << setw( 9 ) ;
   output.setf( ios:: right ) ;

   return output ;
}

ostream& unsetfmt( ostream& output )
{
     output << setw( 0 ) ;
     output.unsetf( ios:: right ) ;
     output << “ degrees.” ;

   return output ;
}


// the class degree abstracts the io and error handling for the reading,
// writing, and calculations of this program.

     // degrees in a full circle.
   const double maxdegrees = 360 ;


class degree
{
   private:

     double      numerator ;
   double      denominator ;

   public:

   // constructors: the default constructor, an initialized constructor,
   // and a copy constructor

   degree() : numerator(0), denominator(0)
   {}

   degree( double num, double dem )
   {
	numerator = num ;
      denominator = dem ;
   }

   // technically, a copy constructor is not required in this case, since
   // compilers will generate a memberwise copy constructor in cases like this.
   // However, anytime arrays are involved, a copy constructor must be defined,
   // since assignment of one array to another actually only copies the address
   // of the array, and not the contents. Then it becomes a race to see which
   // instance of the class deallocates the memory for the array first - the
   // second time the now invalid array gets deallocated the operating system
   // will terminate the program with a GPF (Windows, Win32, OS/2) or a
   // segmentation fault (UNIX, Linux).
   //
   // This being said, it’s good practice to include one anyway.
   degree ( degree& copyfrom )
   {
	numerator = copyfrom.numerator ;
      denominator = copyfrom.denominator ;
   }

   // overloaded assignment operator
   degree& operator=( const degree& copyfrom )
   {
	numerator = copyfrom.numerator ;
      denominator = copyfrom.denominator ;
      return *this ;
   }

   // validity operator ! - dividing by zero is an illegal operation from the
   // computer’s point of view, so we check for this possibility using the
   // NOT operator

   bool operator!() const
   {
	if( denominator == 0.0 ) return true ;
      return false ;
   }

   // this returns the actual degrees
     double val() const
   {
	 if( !*this ) // check to see if our data is valid
	   return 0.0 ;

      return numerator / denominator * maxdegrees ;
   }

};

   // overloaded stream extraction and insertion operators

istream& operator>>( istream& is, degree& read )
{
      double a, b ;
   is >> a >> b ;
   read = degree(a, b);

   return is ;
}

// decision time: we could put the io manipulators into the output stream
// instead, if we wanted to.

ostream& operator<<( ostream& os, const degree& write )
{
     if( !write ) // data is not valid
	  os << “Error!” ;
   else
	os << setfmt << write.val() << unsetfmt ;

   return os ;
}


// the main function actually does the IO handling and printing of the
// copyright.


int main( int, char** )
{

     // the cerr output stream does not get redirected by default,
   // so it’s used to print messages that have to make it though to the user

   cerr      << “ Fraction to angle calculator.” << endl
	     << “ (c) Jan Walter, 1998. NO WARRANTY.” << endl ;

   degree mydegree ;
   int count = 1 ;

   cin >> mydegree ;

   while( cin )
   {
	cout << “ Reading “ << count++ << “ : “ <<  mydegree << endl ;
      cin >> mydegree ;
   }

}
// end of file

Comments

Using custom I/O manipulators can make code more readable, more trouble-free, and also more maintainable. This is probably one of the least-exploited features of the iostreams library. Another advantage is that your program’s output will be more consistent.

The criteria I use is that if I have to repeat a formatting sequence more than three times for a given type of data, it’s worth my time to put the sequence into a custom modifier to ensure that the output of the program will be more consistent.


Previous Table of Contents Next


Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-1999 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permision of EarthWeb is prohibited.