Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

C++ Manipulators and Applicators


OCT93: C++ Manipulators and Applicators

Reg is president of Charney and Day Inc. and a member of ANSI's X3J16 Committee on the C++ language. He is also former chairman of The Canadian Standards Association's Working Group on Program Design Standards (Z243.21). Reg can be reached on CompuServe at 70272,3427.


Manipulators and applicators simplify the use of C++ I/O streams and add elegance to input and output expressions. As a result, the logical connection between functionality and use is more obvious. They also express complex procedures more concisely and minimize name-space pollution. That is, names chosen for manipulators and applicators can also be used for other things, since function-name overloading will distinguish between uses of the same name.

Although the terms manipulator and applicator are commonly used for both functions and classes, this article distinguishes between the two uses. When manipulators and applicators are used without qualification, the name applies to both functions and classes.

The Problem

Using function qStr() and the iostream.h package, you'd expect Example 1(a) to produce the output in Example 1(b). However, the output we actually get is shown in Example 1(c). The reversed output and pointer values are caused by the function in the output expression being evaluated first.

A compiler reduces all components of an expression to data and operator types. Next, the user-defined operators of equal precedence are processed in order. In Example 1(a), all the operators have the same precedence. So, only the function call needs to be replaced by its return type before evaluating the expression.

The solution to the reversed output problem is to make qStr() appear as a data type. The compiler can then evaluate the similar expressions in the expected order.

Manipulator and Applicator Functions

Manipulator functions modify their environment via side affects. Usually, they take and return references to the streams in which they are used. Other arguments normally remain unchanged. Refer to Example 2(a) and 2(b).

An applicator function has two or more arguments, one of which is a function pointer. The applicator invokes the function argument passing the other arguments to it. Its return type can be anything. Example 3 shows an applicator function called af().

Manipulator Classes

Manipulator classes solve the problem of replacing a function call with a data type. These classes encapsulate a manipulator function pointer with its arguments and an applicator function that is recognized in the streams where the manipulator class instances will be used.

Manipulator constructor arguments are stored and used later. The arguments always include a manipulator function pointer. Friend operator functions overload the stream insert and extract operators and allow the compiler to recognize an instance of the manipulator class within a stream expression. These operators are also applicator functions. That is, they invoke the stored manipulator function using the stored arguments. Example 4 shows manipulator class output. (Bjarne Stroustrup refers to manipulator class instances as "function objects.")

Manipulator Interface Functions

In Example 5(a), a manipulator interface function is used to encapsulate the manipulator function and its arguments into an instance of the manipulator class. Example 5(b) shows the output.

With the manipulator functions, manipulator classes, and applicator functions already defined, you're ready to output quoted strings. Example 6 specifies manipulators explicitly. However, every C++ implementation, has a iomanip.h library header file that contains generic input and output manipulator classes. Depending on compiler, they are implemented as macros or as templates. Example 7 uses templates to get the same results as Example 6.

The iomanip.h header depends on the iostream.h header file. Therefore, it must appear after the inclusion of iostream.h. The names of the generic manipulator classes vary with compiler, but most use the names SMANIP, OMANIP, IMANIP and IOMANIP classes for ios, ostream, istream, and iostream stream classes, respectively. By default, all compilers generate manipulator classes to handle int and long manipulator function arguments. They also generate the prototypes for the standard manipulator functions like setw().

If iomanip.h is implemented using macros and you wish to handle arguments of types other than int and long, you must do two additional things. First, ensure that the new type is a single token, like CP in Example 7. The macro preprocessor uses this single token type name to generate other names. Second, you must issue a IOMANIPdeclare statement to generate all the generic manipulator classes for the new type. The new type can be a class. Example 8 uses a macro version of the generic manipulator functions.

Applicator Classes

An applicator class, which is an alternate way of specifying manipulator interface functions, has one or more constructors and one or more function-call operators. The applicator class constructors take one argument--a pointer to a user-defined manipulator function. The pointer is stored for later use.

The applicator function-call operator associates an applicator instance with a set of arguments. The stored function pointer and arguments are used in the manipulator class constructor to create and return an instance of its class. Example 9 shows a typical applicator definition.

As with manipulators, the iomanip.h header file contains generic applicator classes, usually called SAPP, IAPP, OAPP and IOAPP for the ios, istream, ostream, and iostream classes.

Example 10 shows how an applicator class would be used in Example 7 with the template version of the iomanip.h header.

Implementation Details

The parameterless manipulators endl, ends, flush, dec, hex, oct, and ws are predefined in the iostream.h header file. The header iomanip.h defines the int and long manipulators setw(int), setfill(int), and setprecision(int).

The declaration of manipulator interface functions can be placed in header files while a separate file can contain their definitions.

Manipulators for classes derived from I/O stream must be defined carefully. They will also apply to the base-class-streams unless there is a non-member applicator defined for the manipulator and the derived class.

Conclusions

For this article, I used three compilers that support templates: Comeau Computing's C++ v3.0, MetaWare's HIGH C/C++ v3.0, and Borland's C++ v3.1. Only Comeau's C++ comes with a template version of iomanip.h. The other two compilers use macro versions of this header file.

Manipulators and applicators are most often used with the I/O streams package. However, their use can be extended to any type of class which has overloaded operators and whose designers want elegance and clarity.

Acknowledgments

This article was written as part of my response to an article on C++ design by Michael Schelkin of Stins Coman, Moscow, Russia, which appeared in the C Users Group (U.K.) magazine, CVu. He was responding to my article on C++ design in the C++ Journal (vol 2, no. 2).

References

Charney, R.B. "Data Attribute Notation, Part 1." C++ Journal (vol 2 , 1992).

Charney, R.B. "Data Attribute Notation, Part 2." C++ Journal (vol 2 , 1993).

Dewhurst S.C. and K.T. Stark. Programming in C++. Englewood Cliffs, NJ: Prentice Hall, 1989.

Eckel, B. C++ Inside & Out. Berkeley, CA: Osborne McGraw-Hill, 1993.

Jensen Partners International, Topspeed C++ 3.02 Class Library Guide, 1991.

Microsoft C/C++ Version 7.0 Class Libraries User's Guide, 1991.

Stroustrup, B. The C++ Programming Language Second Edition. Reading, MA: Addison-Wesley, 1991.

UNIX System V AT&T C++ Language System Release 2.0 Library Manual, 1989.

Zortech C++ Compiler 3.0 Function Reference Manual, 1991.

Example 1:

(a)
// quote argument string and then
// output it.
ostream& qStr(char* s)
{ return cout<<"'"<<s<<"'"; }
cout << "Output is a " <<
  qStr("string") << ".";
(b)
Output is a 'string'.
(c)
'string'Output is a 0x48f2.

Example 2:

(a)
typedef ostream OS; // an abbrev
// quote argument string and then
// output it.
OS& aMF(OS& os,char* s)
{ return os << "'" << s << "'"; }
aMF(cout, "string");
(b)
outputs on
cout the value:
'string'

Example 3:

typedef long (*FP)(int, int);
// af is applicator function
long af(FP f, int i, int j)
  { return (*f)(i,j); }
long sum(int i, int j)
  { return i+j; }
long dif(int i, int j)
  { return i-j; }
af(sum,2,3); // returns (long)5
af(dif,9,5); // returns (long)4

Example 4:

typedef ostream OS; // an abbrev
template<class T>
class OMC // output manip class
{
  typedef OS& (*MF)(OS&, T);
  MF mf;  // manip function
  T  a;     // arg of type T
public:
  OMC(MF mmf,T aa)
     : mf(mmf), a(aa) { }
  friend OS& operator <<
     (OS& os, const OMC<T>& mc)
  { return (*mc.mf)(os,mc.a); }
};

Example 5:

(a)
// define manipulator interface
// for manipulator function aMF.
OMC<char*> aMI(char* s)
{ return OMC<char*>(aMF,s); }
cout << "Value "<< aMI("string");
(b)
Value 'string'

Example 6:

#include <iostream.h>
typedef ostream OS; // an abbrev
// qSTR - manip function
OS& qSTR(OS& os,char* s)
{ return os << "'" << s << "'"; }
// OMC - Output Manipulator Class
template<class T> class OMC {
  typedef OS& (*MF)(OS&, T);
  MF mf;  // manipulator fcn
  T  a;     // arg of type T
public:
  OMC(MF mmf,T aa)
     : mf(mmf), a(aa) { }
  friend OS& operator <<
     (OS& os, const OMC<T>& mc)
  { return (*mc.mf)(os,omc.a); }
};
// qStr - manip interface
// for manip function qSTR
OMC<char*> qStr(char* s)
{ return OMC<char*>(qSTR,s); }
// sample output expression
cout << "Output is a " <<
    qStr("string") << "\n";

Example 7:

#include <iostream.h>
#include <iomanip.h>
typedef ostream OS; // an abbrev
typedef char* CP;   // single token
// qSTR - manip function
OS& qSTR(OS& os,CP s)
{ return os << "'" << s << "'"; }
// qStr - manip interface
// for manip function qSTR
OMANIP<CP> qStr(CP s)
{ return OMANIP<CP>(qSTR,s); }
// sample output expression
cout << "Output is a "
     << qStr("string") << "\n";

Example 8:

#include <iostream.h>
#include <iomanip.h>
typedef ostream OS; // an abbrev
typedef char* CP; // single token
IOMANIPdeclare(CP);
// qSTR - manip function
OS& qSTR(OS& os,CP s)
{ return os << "'" << s << "'"; }
// qStr - manip interface
// for manipulator fcn qSTR
OMANIP(CP) qStr(CP s)
{ return OMANIP(CP)(qSTR,s); }
// sample output expression
cout << "Output is a "
     << qStr("string") << "\n";

Example 9:

typedef ostream OS; // an abbrev
template<class T> class OAC {
  typedef OS& (*MF)(OS&, T);
  MF mf;
public:
  OAC(MF mmf) : mf(mmf) { }
  OMC<T> operator()(T a)
     { return OMC<T>(mf,a); }
};

Example 10:

#include <iostream.h>
#include <iomanip.h>
typedef ostream OS; // an abbrev
typedef char* CP;   // single token
// qSTR - manip function
OS& qSTR(OS& os,CP s)
{ return os << "'" << s << "'"; }
OAPP<CP> qStr = qSTR;
// sample output expression
cout << "Output is a "
     << qStr("string") << "\n";


Copyright © 1993, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.