Skip to content. Skip to navigation

ICTP Portal

Sections
You are here: Home Manuals on-line PGI Compiler pgC_lib stdlibug 2.8 Manipulators
Personal tools
Document Actions

2.8 Manipulators



Click on the banner to return to the user guide home page.

2.8 Manipulators

We have seen examples of manipulators in Section 2.3.3.2. There we learned that:

  1. Manipulators are objects that can be inserted into or extracted from a stream, and

  2. Such insertions and extractions have specific desirable side effects.

As a recap, here is a typical example of two manipulators:

cout << setw(10) << 10.55 << endl;

The inserted objects setw(10) and endl are the manipulators. As a side effect, the manipulator setw(10) sets the stream's field width to 10. Similarly, the manipulator endl inserts the end of line character and flushes the output.

As we have mentioned previously, extensibility is a major advantage of iostreams. We've seen in the previous section how you can implement inserters and extractors for user-defined types that behave like the built-in input and output operations. Additionally, you can add user-defined manipulators that fit seamlessly into the iostreams framework. In this section, we will see how to do this.

First of all, to be extracted or inserted, a manipulator must be an object of a type that we call manipT, for which overloaded versions of the shift operators exist. (Associated with the manipulator type manipT, there is usually a function that we will call fmanipT()that we will explain in detail later.) Here's the pattern for the manipulator extractor:

template <class charT, class Traits>
basic istream<charT,Traits>&
operator>> (basic istream<charT,Traits>& istr
           ,const manipT& manip)
{   return fmanipT(istr, _);   }

With this extractor defined, you can extract a manipulator Manip, which is an object of type manipT, by simply saying:

cin >> Manip;

This results in a call to the operator>>() sketched out above. The manipulator inserter is analogous.

A manipulator's side effect is often created by calling an associated function that takes a stream and returns the stream. There are several ways to associate the manipulator type manipT to the function that we will explore in the subsequent sections. The iostream framework does not specify a way to implement manipulators, but there is a base class called smanip that you can use for deriving your own manipulators. We will explain this technique along with other useful approaches.

It will turn out that there is a major difference between manipulators with parameters like width(10) and manipulators without parameters like endl. Let's start with the simpler case of manipulators without parameters.

2.8.1 Manipulators without Parameters

Manipulators that do not have any parameters, like endl, are the simplest form of manipulator. The manipulator type manipT is a function pointer type, the manipulator Manip is the function pointer, and the associated function is the function pointed to.

In iostreams, the following function pointer types serve as manipulators:

(1) ios_base&                    (*pf)(ios_base&)
(2) basic_ios<charT,Traits>&     (*pf)(basic_ios<charT,Traits>)
(3) basic_istream<charT,Traits>& (*pf)(basic_istream<charT,Traits>)
(4) basic_ostream<charT,Traits>& (*pf)(basic_ostream<charT,Traits>)

The signature of a manipulator function is not limited to the examples above. If you have created your own stream types, you will certainly want to use additional signatures as manipulators.

For the four manipulator types listed above, the stream classes already offer the required overloaded inserters and member functions. For input streams, extractors take the following form:

template<class charT, class traits>
basic_istream<charT, traits>&
basic_istream<charT, traits>::operator>>
(basic_istream<charT,traits>& (*pf)(input_stream_type&) )
{  return (*pf)(*this);..}

where input_stream_type is one of the function pointer types (1)-(3).

Similarly, for output streams we have:

template<class charT, class traits>
basic_ostream<charT, traits>&
basic_ostream<charT, traits>::operator<<
(basic_ostream<charT, traits>& (*pf)(output_stream_type& ))
{  return (*pf)(*this);  }

where output_stream_type is one of the function pointer types (1), (2), or (4).

2.8.1.1 Examples of Manipulators without Parameters

Let's look at the manipulator endl as an example of a manipulator without parameters. The manipulator endl, which can be applied solely to output streams, is a pointer to the following function of type (4):

template<class charT, class traits>
inline basic_ostream<charT, traits>&
endl(basic_ostream<charT, traits>& os)
{
  os.put( os.widen('\n') );
  os.flush();
 
  return os;
}

Hence an expression like:

cout << endl; 

results in a call to the inserter:

ostream& ostream::operator<< (ostream& (*pf)(ostream&))

with endl as the actual argument for pf. In other words, cout << endl; is equal to cout.operator<<(endl);

Here is another manipulator, boolalpha, that can be applied to input and output streams. The manipulator boolalpha is a pointer to a function of type (1):

ios_base& boolalpha(ios_base& strm)
{
  strm.setf(ios_base::boolalpha);
 
  return strm;
}
 

Summary: Every function that takes a reference to an ios_base, a basic_ios, a basic_ostream, or a basic_istream, and returns a reference to the same stream, can be used as a parameter-less manipulator.


2.8.1.2 A Remark on the Manipulator endl

The manipulator endl is often used for inserting the end-of-line character into a stream. However, endl does additionally flush the output stream, as you can see from the implementation of endl shown above. Flushing a stream, a time-consuming operation that decreases performance, is unnecessary in most common situations. In the standard example:

cout << "Hello world" << endl;

flushing is not necessary because the standard output stream cout is tied to the standard input stream cin, so input and output to the standard streams are synchronized anyway. Since no flush is required, the intent is probably to insert the end-of-line character. If you consider typing '\n' more trouble than typing endl, you can easily add a simple manipulator nl that inserts the end-of-line character, but refrains from flushing the stream.

2.8.2 Manipulators with Parameters

Manipulators with parameters are more complex than those without because there are additional issues to consider. Before we explore these issues in detail and examine various techniques for implementing manipulators with parameters, let's take a look at one particular technique, the one that is used to implement standard manipulators such as setprecision(), setw(), etc.

2.8.2.1 The Standard Manipulators

Rogue Wave's implementation of the standard iostreams uses a certain technique for implementing most standard manipulators with parameters: the manipulator type manipT is a function pointer type; the manipulator object is the function pointed to; and the associated function is a global function.

The C++ standard defines the manipulator type as smanip. The type itself is implementation-defined; all you know is that it is returned by some of the standard manipulators. In Rogue Wave's implementation, smanip is a class template:

template<class T>
class smanip {
  public:
    smanip(ios_base& (*pf)(ios_base&, T), T manarg);
};

A standard manipulator like setprecision() can be implemented as a global function returning an object of type smanip<T>:

inline smanip<int> setprecision(int n)
{ return smanip<int>(sprec, n); }

The associated function is the global function sprec:

inline ios_base& sprec(ios_base& str, int n)
{
  str.precision(n);
  return str;
}

2.8.2.2 The Principle of Manipulators with Parameters

The previous section gave an example of a technique for implementing a manipulator with one parameter: the technique used to implement the standard manipulators in iostreams. However, this is not the only way to implement a manipulator with parameters. In this section, we examine other techniques. Although all explanations are in terms of manipulators with one parameter, it is easy to extend the techniques to manipulators with several parameters.

Let's start right at the beginning: what is a manipulator with a parameter?

A manipulator with a parameter is an object that can be inserted into or extracted from a stream in an expression like:

cout << Manip(x);
cin  >> Manip(x);

Manip(x) must be an object of type manipT, for which the shift operators are overloaded. Here's an example of the corresponding inserter:

template <class charT, class Traits>
basic ostream<charT,Traits>&
operator<< (basic ostream<charT,Traits>& ostr
           ,const manipT& manip)
{  // call the associated function , e.g.
   (*manip. )(ostr,manip.argf);
   return os;
}

With this inserter defined, the expression cout << Manip(x); is equal to a call to the shift operator sketched above; i.e., operator<<(cout, Manip(x));

Assuming that a side effect is created by an associated function , the manipulator must call the associated function with its respective argument(s). Hence it must store the associated function together with its argument(s) for a call from inside the shift operator.

The associated function can be a static or a global function, or a member function of type manipT, for example.

In the inserter above, we've assumed that the associated function is a static or a global function, and that it takes exactly one argument. Generally, the manipulator type manipT might look like this:

template <class FctPtr, class Arg1, class Arg2, _>
class manipT
{
 public:
   manipT(FctPtr, Arg1, Arg2, _);
 private:
   FctPtr fp_;
   Arg1   arg1_;
   Arg2   arg2_;
};

Note that this is only a suggested manipulator, however. In principle, you can define the manipulator type in any way that makes the associated side effect function and its arguments available for a call from inside the respective shift operators for the manipulator type. We will see other examples of such manipulator types later in this section; for instance, a manipulator type called smanip defined by the C++ standard. It is an implementation-defined function type returned by the standard manipulators. See the Class Reference for details.

Returning now to the example above, the manipulator object provided as an argument to the overloaded shift operator is obtained by Manip(x), which has three possible solutions:

  1. Manip(x) is a function call. In this case, Manip would be the name of a function that takes an argument of type x and returns a manipulator object of type manipT; i.e., Manip is a function with the following signature:

    manipT Manip (X x);
    
  2. Manip(x) is a constructor call. In this case, Manip would be the name of a class with a constructor that takes an argument of type X and constructs a manipulator object of type Manip; i.e., Manip and manipT would be identical:

    class Manip {
     public:
       Manip(X x);
    };
    
  3. Manip(x) is a call to a function object. In this case, Manip would be an object of a class M, which defines a function call operator that takes an argument of type x and returns a manipulator object of type manipT:

    class M {
     public:
       manipT operator()(X x);
    } Manip;
    

Solutions (1) and (2) are semantically different from solution (3). In solution (1), Manip is a function and therefore need not be created by the user. In solution (2), Manip is a class name and an unnamed temporary object serves as manipulator object. In solution (3), however, the manipulator object Manip must be explicitly created by the user. Hence the user has to write:

manipT Manip;
cout << Manip(x);

which is somewhat inconvenient because it forces the user to know an additional name, the manipulator type manipT, and to create the manipulator object Manip.[31] For these reasons, solution (3) is useful if the manipulator has state; i.e., if it stores additional data like a manipulator, let call it lineno, which provides the next line number each time it is inserted.

The problem with solution (2) is that several current compilers cannot handle unnamed objects.

For any of the three solutions just discussed, there is also a choice of associated functions. The associated function can be either:

    a) A static or a global function;

    b) A static member function;

    c) A virtual member function.

Among these choices, (b), i.e. use of a static member function, is the preferable in an object-oriented program because it permits encapsulation of the manipulator together with its associated function. This is particularly recommended if the manipulator has state, as in solution (3), where the manipulator is a function object, and the associated function has to access the manipulator's state.

Using ( c), i.e. a virtual member function, introduces the overhead of a virtual function call each time the manipulator is inserted or extracted. It is useful if the manipulator has state, and the state needs to be modified by the associated manipulator function. A static member function would only be able to access the manipulator's static data; a non-static member function, however, can access the object-specific data.

2.8.2.3 Examples of Manipulators with Parameters

In this section, let's look at some examples of manipulators with parameters. The examples here are arbitrary combinations of solutions (1) to (3) for the manipulator type, with (a) to (c) for the associated function. We also use the standard manipulator setprecision()to demonstrate the various techniques.

Example 1: Function Pointer and Global Function. This example combines (1) and (c), and so:

  • manipT is a function pointer type;

  • The manipulator object is the function pointed to; and

  • The associated function is a global function.

Rogue Wave's implementation of the standard iostreams uses this technique for implementing most standard manipulators with parameters. See Section 2.8.2.1 for reference.

Example 2: Unnamed Object and Static Member Function. This example combines (2) and (b), and thus:

  • The manipulator object Manip is an unnamed object;

  • The manipulator type manipT is a class; and

  • The associated function is a static member function.

The manipulator type manipT can be derived from the manipulator type smanip defined by iostreams. Here is an alternative implementation of a manipulator like setprecision():

class setprecision : public smanip<int> {
  public:
    setprecision(int n) : smanip<int>(sprec_, n) { }
  private:
    static ios_base& sprec_(ios_base& str, int n)
    { str.precision(n);
      return str;
    }
};

Example 3: Unnamed Object and Virtual Member Function. This example (2) and (c), and therefore:

  • The manipulator object Manip is an unnamed object;

  • The manipulator type manipT is a class; and

  • The associated function is a virtual member function of that class.

The idea here is that the associated function is a non-static member function of the manipulator type manipT. In such a model, the manipulator does not store a pointer to the associated function , but defines the associated function as a pure virtual member function. Consequently, the manipulator type manipT will be an abstract class, and concrete manipulator types will be derived from this abstract manipulator type. They will have to implement the virtual member function that represents the associated function.

Clearly, we need a new manipulator type because the standard manipulator type smanip is implementation-defined. In Rogue Wave's Standard C++ Library, it has no virtual member functions, but stores a pointer to the associated function. Here is the abstract manipulator type we need:

template <class Arg, class Ostream>
class virtsmanip
{
 public:
  typedef Arg argument_type;
  typedef Ostream ostream_type;
  virtsmanip (Arg a) : arg_(a) { }
 
 protected:
  virtual Ostream& fct_(Ostream&,Arg) const = 0;
  Arg arg_;
 
  friend Ostream&
  operator<< (Ostream& ostr
             ,const virtsmanip<Arg,Ostream>& manip);
};

This type virtsmanip differs from the standard type smanip in several ways:

  • It defines the above-mentioned pure virtual member function fct_().

  • The argument arg_ and the virtual function fct_() are protected members, and consequently the respective shift operator for the manipulator type has to be a friend function.

  • It is a base class for output manipulators only.

The standard manipulator smanip expects a pointer to a function that takes an ios_base reference. In this way, a manipulator is always applicable to input and output streams, regardless of whether or not this is intended. With our new manipulator type virtsmanip, we can define manipulators that cannot inadvertently be applied to input streams.

Since we have a new manipulator type, we also need a new overloaded version of the manipulator inserter:

template <class Arg, class Ostream>
Ostream& 
operator<< (Ostream& ostr, const virtsmanip<Arg,Ostream>& manip)
{
   manip.fct_(ostr,manip.arg_);
   return ostr;
}

After these preparations, we can now provide yet another alternative implementation of a manipulator like setprecision(). This time setprecision() is a manipulator for output streams only:

class setprecision : public virtsmanip<int,basic_ostream<char> > 
{
public:
 setprecision(argument_type n) 
 : virtsmanip<argument_type,ostream_type>(n) { }
 
protected:
 ostream_type& fct_(ostream_type& str, argument_type n) const
 {
   str.precision(n);
   return str;
 }
}; 

Example 4: Function Object and Static Member Function. The next example combines (3) and (b), so here:

  • The manipulator object Manip is an object of a type M that defines the function call operator;

  • The manipulator type manipT is a class type that is returned by the overloaded function call operator of class M; and

  • The associated function is a static member function of class M.

This solution, using a function object as a manipulator, is semantically different from the previous solution in that the manipulator object has state, i.e., it can store data between subsequent uses.

Let us demonstrate this technique in terms of another example: an output manipulator that inserts a certain string that is maintained by the manipulator object. Such a manipulator could be used, for instance, to insert a prefix to each line:

Tag<char> change_mark("v1.2 >> ");

while ( new_text )
   ostr << change_mark << next_line;

change_mark("");
while ( old_text)
   ostr << change_mark << next_line;

We would like to derive the Tag manipulator here from the standard manipulator smanip. Unfortunately, smanip is restricted to associated functions that take an ios_base reference as a parameter. In our example, we want to insert the stored text to the stream, so we need the stream's inserter. However, ios_base does not have inserters or extractors. Consequently we need a new manipulator base type, similar to smanip, that allows associated functions that take a reference to an output stream:

template <class Ostream, class Arg>
class osmanip {
  public:
    typedef Ostream ostream_type;
    typedef Arg argument_type;
 
    osmanip(Ostream& (*pf)(Ostream&, Arg), Arg arg)
    : pf_(pf) , arg_(arg) { ; }
 
  protected:
    Ostream&     (*pf_)(Ostream&, Arg);
    Arg          arg_;
 
  friend Ostream&
    operator<< 
    (Ostream& ostr, const osmanip<Ostream,Arg>& manip);
};

Then we need to define the inserter for the new manipulator type osmanip:

template <class Ostream, class Arg>
Ostream& 
operator<< (Ostream& ostr,const osmanip<Ostream,Arg>& manip)
{
   (*manip.pf_)(ostr,manip.arg_);
   return ostr;
}

Now we define the function object type M, here called Tag:

template <class  charT>
class Tag 
: public osmanip<basic_ostream<charT>, basic_string<charT> >
{
public:
 Tag(argument_type a = "")
  : osmanip<basic_ostream<charT>, basic_string<charT> >
    (fct_, a) { }
 
 osmanip<ostream_type,argument_type>&
  operator() (argument_type a)
  {
    arg_ = a;
    return *this;
  }
 
private:
 static ostream_type& fct_ (ostream_type& str, argument_type a)
 { 
   return str << a;
 }
};

Note that the semantics of this type of manipulator differ from the previous ones, and from the standard manipulator setprecision. The manipulator object has to be explicitly created before it can be used, as shown in the example below:

Tag<char> change_mark("v1.2 >> ");

while ( new_text )
   ostr << change_mark << next_line;

change_mark("");
while ( old_text)
   ostr << change_mark << next_line;

This kind of manipulator is more flexible. In the example above, you can see that the default text is set to "v1.2 >> " when the manipulator is created. Thereafter you can use the manipulator as a parameterless manipulator and it will remember this text. You can also use it as a manipulator taking an argument, and provide it with a different argument each time you insert it.

Example 5: Function Object and Virtual Member Function. In the previous example, a static member function is used as the associated function. This has the slight disadvantage that the associated function cannot modify the manipulator's state. Should modification be necessary, you might consider using a virtual member function instead.

Our final example here is a manipulator that stores additional data, the previously mentioned lineno manipulator. It adds the next line number each time it is inserted:

LineNo lineno;
while (!cout)
{
   cout << lineno << _;
}

The manipulator is implemented following the (3) and (b) pattern, i.e.:

  • The manipulator object Manip is an object of a type M that defines the function call operator;

  • The manipulator type manipT is a class type that is returned by the overloaded function call operator of class M; and

  • The associated function is a virtual member function of class M.

The manipulator object contains a line number that is initialized when the manipulator object is constructed. Each time the lineno manipulator is inserted, the line number is incremented.

For the manipulator base type, we use a slightly modified version of the manipulator type osmanip from Example 3. The changes are necessary because the associated function in this case may not be a constant member function:

emplate <class Arg, class Ostream>
class virtsmanip
{
 public:
  typedef Arg argument_type;
  typedef Ostream ostream_type;
  virtsmanip (Arg a) : arg_(a) { }
 
 protected:
  virtual Ostream& fct_(Ostream&,Arg) = 0;
  Arg arg_;
 
  friend Ostream&
  operator<< (Ostream& ostr
             ,virtsmanip<Arg,Ostream>& manip);
};

template <class Arg,class Ostream>
Ostream&
operator<< (Ostream& ostr
           ,virtsmanip<Arg,Ostream>& manip)
{
   manip.fct_(ostr,manip.arg_);
   return ostr;
}

The line number manipulator could be implemented like this:

template <class Ostream>
class LineNo
 : public virtsmanip<int,Ostream >
{
public:
 LineNo(argument_type n=0) 
  : virtsmanip<argument_type, ostream_type> (n) 
  { }
 
 virtsmanip<argument_type,ostream_type>&
  operator() (argument_type arg)
  {
   arg_ = arg;
   return *this;
  }
protected:
 argument_type lno_;
 ostream_type& fct_ (ostream_type& str, argument_type n)
 { 
   lno_ = (n>0) ? n : lno_;
   str << ++lno_;
   arg_ = -1;
   return str;
 }
};

Using a virtual member function as the associated manipulator function introduces the overhead of a virtual function call each time the manipulator is inserted. If it is necessary that a manipulator update its state after each insertion, a static member function will not suffice. A global function that is a friend of the manipulator type might do the trick. However, in an object-oriented program, you are usually advised against global functions that modify private or protected data members of a class they are friends with.


©Copyright 1996, Rogue Wave Software, Inc.

Weather
No information available
 

Powered by Plone This site conforms to the following standards: