DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
iostream examples

Extending state variables

In many circumstances we would like to add state variables to streams. For example, suppose we are printing trees and would like to have an indentation level associated with an ostream.

   int xdent = ios::xalloc() ;
   	// generate a unique index
   

ostream& indent(ostream& o) { // manipulator that inserts newlines and // appropriate number of tabs o << '\n' ; int count = o.iword(xdent) ; while ( count-- > 0 ) o << '\t' ; return o ; }

ostream& redent(ostream& o, int n) { // parameterized manipulator that modifies // indentation level o.iword(xdent) += n ; }

OAPP(int) redent = redent ;

o.iword(xdent) is a reference to the xdent'th integer state variable. Each call to ios::xalloc returns a different index. The index may then be used to access a word associated with the stream. The reason for calling ios::xalloc to get an index rather than just picking an arbitrary one is that it allows combining code that uses the indentation level with code that may have extended the formatting state variables for some other purpose.

A subtle problem occurs in the above example because xdent is initialized by a function call. What if indent() or redent() were called before xdent was initialized? Can that happen? Yes it can. It can happen if indent() or redent() is called from inside a constructor that is itself called to initialize some variable with program extent. Problems with order of initialization when doing I/O in constructors are common. The solution relies on ``tricks'' to force initialization order. In this case we would put into the header file containing the declarations of indent() and redent():

   static class Indent_init {
   	static int count ;
   public:
   		Indent_init() ;
   		~Indent_init() ;
   } indent_init ;

Each file that includes this header file will have a local variable indent_init that has to be initialized. Because this variable is declared in the header its initialization will occur early.

The definition of the constructor and destructor looks like:

   static iostream_init* io ;
   

Indent_init::Indent_init() { // count keeps track of the difference between how // many constructor and destructor calls there are if ( count++ > 0 ) return ;

// This code is executed only the first time io = new iostream_init ; xdent = ios::xalloc() ; }

Indent_init::~Indent_init() { if ( --count ) > 0 ) return ;

// This code will be executed the last time delete io ; }

The iostream library uses this idea itself. The constructor for iostream_init causes the iostream library to be initialized the first time it is called. It also keeps track of how many times the constructor is called and will do finalization operations on various data structures the last time it is called. It is therefore important that any values of type iostream_init that are constructed by a program are eventually deleted. This is the purpose of having an Indent_init destructor; even though there are no finalization operations associated with indentation, it must delete io.
Next topic: Comparison of iostreams and stdio
Previous topic: Specializing istream or ostream

© 2004 The SCO Group, Inc. All rights reserved.
UnixWare 7 Release 7.1.4 - 27 April 2004