Home arrow C++ arrow Page 2 - Manipulating Streams and Files with C++
C++

Manipulating Streams and Files with C++


In this second part of a five-part series on handling streams and files in C++, you will learn how to format floating-point output, how to write your own stream manipulators, and more. This article is excerpted from chapter 10 of the C++ Cookbook, written by Ryan Stephens, Christopher Diggins, Jonathan Turkanis and Jeff Cogswell (O'Reilly; ISBN: 0596007612). Copyright © 2007 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Author Info:
By: O'Reilly Media
Rating: 5 stars5 stars5 stars5 stars5 stars / 1
October 23, 2008
TABLE OF CONTENTS:
  1. · Manipulating Streams and Files with C++
  2. · 10.3 Writing Your Own Stream Manipulators
  3. · 10.4 Making a Class Writable to a Stream
  4. · 10.5 Making a Class Readable from a Stream

print this article
SEARCH DEVARTICLES

TOOLS YOU CAN USE

advertisement
Manipulating Streams and Files with C++ - 10.3 Writing Your Own Stream Manipulators
(Page 2 of 4 )

Problem

You need a stream manipulator that does something the standard ones can't. Or, you want to have a single manipulator set several flags on the stream instead of calling a set of manipulators each time you want a particular format.

Solution

Writing Your Own Stream Manipulators 

To write a manipulator that doesn't take an argument (a` la left), write a function that takes an ios_base parameter and sets stream flags on it. If you need a manipulator that takes an argument, see the discussion a little later. Example 10-4 shows how to write a manipulator that doesn't take an argument.

Example 10-4. A simple stream manipulator

#include <iostream>
#include <iomanip>
#include <string>

using namespace std;

// make floating-point output look normal
inline ios_base& floatnormal(ios_base& io) {
  
io.setf(0, ios_base::floatfield);
  
return(io);
}
int main() {

   ios_base::fmtflags flags = // Save old flags
      cout.flags();

   double pi = 22.0/7.0;

   cout << "pi = " << scientific    // Scientific mode
        << pi * 1000 << '\n';

   cout << "pi = " << floatnormal
        << pi << '\n';

   cout.flags(flags);
}

Discussion

There are two kinds of manipulators: those that accept arguments and those that don't. Manipulators that take no arguments are easy to write. All you have to do is write a function that accepts a stream parameter, does something to it (sets a flag or changes a setting), and returns it. Writing a manipulator that takes one or more arguments is more complicated because you need to create additional classes and functions that operate behind the scenes. Since argument-less manipulators are simple, let's start with those.

After reading Recipe 10.1, you may have realized that there are three floating-point formats and only two manipulators for choosing the format. The default format doesn't have a manipulator; you have to set a flag on the stream to get back to the default format, like this:

  myiostr.setf(0, ios_base::floatfield);

But for consistency and convenience, you may want to add your own manipulator that does the same thing. That's what Example 10-4 does. The floatnormal manipulator sets the appropriate stream flag to output floating-point data in the default format.

The compiler knows what to do with your new function because the standard library already defines an operator for basic_ostream (basic_ostream is the name of the class template that ostream and wostream are instantiations of) like this:

  basic_ostream<charT,traits>& operator<<
  (basic_ostream<charT,traits>& (* pf)(basic_ostream<charT,traits>&))

In this example, pf is a pointer to a function that takes a basic_ostream reference argument and returns a basic_ostream reference. This operator just calls your function with the current stream as an argument.

Writing manipulators that take arguments is more complicated. To understand why, consider how a manipulator without arguments works. When you use a manipulator like this:

  myostream << myManip << "foo";

You use it without parenthesis, so that it actually resolves to the address of your manipulator function. operator<< is what actually calls the manipulator function, and it passes in the stream so the manipulator can do its work.

For the sake of comparison, say you have a manipulator that takes a numeric argument, so that, ideally, you would use it like this:

  myostream << myFancyManip(17) << "apple";

How is this going to work? If you assume myFancyManip is a function that takes an integer argument, then there is a problem: How do you pass the stream to the function without including in the parameters and using it explicitly? Here's what you might do:

  myostream << myFancyManip(17, myostream) << "apple";

But this is ugly and redundant. One of the conveniences of a manipulator is the ability to just add it in line with a bunch of operator<< s and to read and use it easily.

The solution is to send the compiler on a detour. Instead of operator<< just invoking your manipulator function on the stream, you need to introduce an ephemeral object that returns something operator<< can use. Here's how.

First, you need to define a temporary class to do the work. For the sake of simplicity, say you want to write a manipulator called setWidth that does the same thing as setw. The temporary structure you need to build should look something like this:

  class WidthSetter {

  public:
   
WidthSetter (int n) : width_(n) {}
   
void operator()(ostream& os) const {os.width(width_);}
 
private:
   
int width_;
  };

The function of this class is simple. Construct it with an integer argument, and when operator() is invoked with a stream argument, set the width on the stream to the value that the object was initialized with. The point of this behavior is that WidthSetter will be constructed by one function and used by another. Your manipulator function is what will construct it, and it should look like this:

  WidthSetter setWidth(int n) {
    return(WidthSetter(n));  // Return the initialized object
  }

All this does is return a WidthSetter object that was initialized with the integer value. This is the manipulator that you will use in line with
operator<<s, like this:

  myostream << setWidth(20) << "banana";

But this alone is not enough, because if setWidth just returns a WidthSetter object, operator<< won't know what to do with it. You have to overload operator<< so it knows how to handle a WidthSetter:

  ostream& operator<<(ostream& os, const WidthSetter& ws) {
   
ws(os);     // Pass the stream to the ws object
   
return(os); // to do the real work
  }

That solves the problem, but in a nongeneric way. You don't want to have to write a WidthSetter-style class for every argument-accepting manipulator you write (maybe you do, but never mind that), so a better approach is to use templates and function pointers to make a nice, generic infrastructure on which you can base any number of manipulators. Example 10-5 provides the ManipInfra class and a version of operator<< that uses template arguments to deal with the different kinds of characters a stream may handle and the different kinds of arguments a stream manipulator might use.

Example 10-5. Manipulator infrastructure

#include <iostream>
#include <string>

using namespace std;

// ManipInfra is a small, intermediary class that serves as a utility
// for custom manipulators with arguments. Call its constructor with a
// function pointer and a value from your main manipulator function.
// The function pointer should be a helper function that does the
// actual work. See examples below. template<typename T, typename C>
class ManipInfra {

public:
  ManipInfra (basic_ostream<C>& (*pFun)
              (basic_ostream<C>&, T), T val)
    : manipFun_(pFun), val_(val) {}
  void operator()(basic_ostream<C>& os) const
     {manipFun_(os, val_);}  // Invoke the function pointer with the
private:                     // stream and value
  T val_;
 
basic_ostream<C>& (*manipFun_)
     (basic_ostream<C>&, T);
};

template<typename T, typename C> basic_ostream<C>& operator<<(basic_ostream<C>& os,
                         
const ManipInfra<T, C>& manip) {
  manip(os);
  return(os);
}

// Helper function that is ultimately called by the ManipInfra class
ostream& setTheWidth(ostream& os, int n) {
   os.width(n);
   return(os);
}

// Manipulator function itself. This is what is used by client
// code
ManipInfra<int, char> setWidth(int n) {
  
return(ManipInfra<int, char>(setTheWidth, n));
}

// Another helper that takes a char argument
ostream& setTheFillChar(ostream& os, char c) {
   os.fill(c);
   return(os);
}

ManipInfra<char, char> setFill(char c) {
   return(ManipInfra<char, char>(setTheFillChar, c));
}

int main() {

  cout << setFill('-')
       << setWidth(10) << right << "Proust\n";
}

If the sequence of events is still hazy, I suggest running Example 10-5 in the debugger. Once you see it in action, it will make perfect sense.


blog comments powered by Disqus
C++ ARTICLES

- Intel Threading Building Blocks
- Threading Building Blocks with C++
- Video Memory Programming in Text Mode
- More Tricks to Gain Speed in Programming Con...
- Easy and Efficient Programming for Contests
- Preparing For Programming Contests
- Programming Contests: Why Bother?
- Polymorphism in C++
- Overview of Virtual Functions
- Inheritance in C++
- Extending the Basic Streams in C++
- Using Stringstreams in C++
- Custom Stream Manipulation in C++
- General Stream Manipulation in C++
- Serialize Your Class into Streams in C++

Dev Articles Forums 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Contact Us 
Site Map 
Privacy Policy 
Support 



© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 9 - Follow our Sitemap
Popular Web Development Topics
All Web Development Tutorials