f8742848 00338
f8742848 00338
THE STANDARD.
A
;.
LIBRARY
~J. Plauger
Prentice Hall P T R
Englewood Cliffs, New Jersey 07632
Editorial/production supervision: Mary Rottino
Manufacturing buyer: Alexis Heydt
Cover design: Aren Graphics
The author and publisher have used their best efforts in preparing this book. These efforts
include the development, research, and testing of the programs to determine their effective-
ness. The author and publisher make no warranty of any kind, expressed or implied, with
regard to these programs or the documentation contained in this book. The author and pub-
lisher shall not be liable in any event for incidental or consequential damages in connection
with, or arising out of, the furnishing, performance, or use of these programs.
All rights reserved. No part of this book may be reproduced, in any form or by any means,
without permission in writing of the author. You may use and redistribute the code frag-
ments in this book without royalty or fee only as part of executable images, and only pro-
vided that the following notice is included prominently in the associated documentation
and as part of the executable image:
For additional licensing of the code, see page xv. The code version shown here is 2.1.
ISBN 0-13-117003-1
TRADEMARKS
TYPOGRAPHY
This book was typeset in Palatino, Avant Garde, and Courier bold by the author using a
Gateway2000 Nomad 425DXL computer running Ventura Publisher 4.1.1
under Microsoft Windows 3.1.
Contents
Preface xi
The Code xiv
Acknowledgments xv
Chapter 0: Introduction 1
Background 1
What the Draft C++ Standard Says 5
Future Directions 14
Using the Library 16
Implementing the Library 21
Testing the Library 25
Exercises 28
Chapter 1: Standard C Library 29
Background 29
What the Draft C++ Standard Says 29
Future Directions 32
Using the Standard C Library 33
Implementing the Standard C Library 36
Testing the Standard C Library 38
Exercises 39
Chapter 2: <defines> 41
Background 41
What the Draft C++ Standard Says 41
Future Directions 42
Using <defines> 43
Implementing <defines> 47
Testing <defines> 48
Exercises 48
Chapter 3: <exception> 51
Background 51
What the Draft C++ Standard Says 55
Future Directions 63
Using <exception> 65
Implementing <exception> 67
Testing <exception> 77
Exercises 80
vi
Chapter 4: <new> 81
Background 81
What the Draft C++ Standard Says 85
Future Directions 88
Using <new> 88
Implementing <new> 89
Testing <new> 92
Exercises 94
Chapter 5: <typeinfo> 95
Background 95
What the Draft C++ Standard Says 96
Future Directions 98
Using <typeinfo> 99
Implementing <typeinfo> 99
Testing <typeinfo> 100
Exercises 102
Chapter 6: <ios> 103
Background 103
What the Draft C++ Standard Says 108
Future Directions 119
Using <ios> 120
Implementing <ios > 128
Testing <ios> 143
Exercises 143
Chapter 7: <streambuf> 147
Background 147
What the Draft C++ Standard Says 149
Future Directions 159
Using <streambuf> 159
Implementing <streambuf> 166
Testing <streambuf> 170
Exercises 171
Chapter 8: <istream> 173
Background 173
What the Draft C++ Standard Says 179
Future Directions 188
Using <istream> 188
Implementing <istream> 195
Testing <istream> 214
Exercises 215
vii
This book shows you how to lIse the library classes and functions
mandated by the draft ANSI/ISO Standard for the programming language
C++. I have chosen to fOClIS on the library exclusively, since many other
books describe the language proper. In fact, remarkably little has been
written ab01.1t the basic library that accompanies nearly every C++ transla-
tor to date. The library portion of the draft C++ Standard describes what
'lvill accompany all implelnentations of C++ in the near future. On that
topic, practically nothing at all has been written, outside of occasional
magazine articles. That's because the draft has only recently coalesced to
the point where its basic intent is clear and precise enough to describe.
Informal The watershed event was the pllblication, in February 1994, of the draft
Review Draft C++ Standard for informal public review. (See Koe94 in Appendix D:
References.) I refer to it here as the Informal Review Draft. This draft marks
the first tin1e that the entire language and library have been described
completely and in one place. It also marks the first time tl1at the draft C++
Sta11dard has been made available to the public in any sort of official
Inanner. Until now, much of the Inaterial has not been spelled out, or has
been available only as working papers within ANSI committee X3J16 or
ISO committee WG21, the groups charged with developing the C++ Stand-
ard. Hence, the Informal Review Draft serves as the basis for this book.
You will find here a cOlnplete and verbatiln presentation of the library
portion of the Informal Review Draft. As the editor of that portion, I
brought together in a Inore uniform style all the separate proposals ap-
proved for inclusion in the Standard C++ library. The presentation here
differs from the Informal Review Draft only in matters of typesetting.
Naturally, the draft C++ Standard continlles to evolve. But the major
changes adopted since the Inforn1al Review Draft add features not tradi-
tionally part of InostC++ impleu1entations. These new features rely heavily
on template technology and extensions that are just beginning to get good
commercial support. I believe that tl1is book captures vvell the existing
practice represented in the Standard c++ library. It is the part that is stable
enough to describe and to start using. Even those parts still likely to change
in outward appearance illustrate functionality that is not likely to disappear.
I include in each chapter a section on Future Directions, to describe changes
already made or under seriolls consideration.
xii Preface
The book also shows you how to implement the Standard C++ library. I
present about 9,000 lines of tested, working code that is designed to be
portable across the most popular current implementations of C++. To a
large extent, the code presented here works atop any ANSI/ISO Standard
C library: It is designed to work particularly well, however, with an en-
hanced version of the portable implementation I provide in my earlier
book, The Standard C Library (Pla92). That work was also published by
Prentice-Hall, in 1992. (Similarities between this Preface and the one in that
book are neither accidental nor a matter of casual copying.)
One way or another, you can use the code presented here to gain valuable
early experience using the library that is destined to become the Standard
for the C++ world. Equally important, I believe that seeing a realistic
implementation of the Standard C++ library can help you better under-
stand how to use it.
And that introduces yet another goal. Besides presenting the Informal
Review Draft for the libral)T, and working code to implement it, this book
also serves as a tutorial on how to use the library: You will find here useful
background information on how the library came to take its present form,
how it was meant to be used, and how it should be used. You don't have to
read and understand all the code presented here to achieve that basic goal.
Even a cursory study is beneficial. You certainly don't have to be a sophis-
ticated user to profit from this book, but the programmer who is just an
occasional sophisticate will find the information presented here invaluable.
Teaching you how to write C++ is not a goal of this book. I assume you
know enough about C++ to read straightforward code. Where the code
presented is not so straightforward, I explain the trickery involved.
the The Standard C++ library is fairly ambitious. It provides considerable
Standard power in many different environments, through an assortment of utilitar-
C++ library ian classes and templates. It promises well-defined name spaces for both
user and implementor. It imposes fairly strict requirements on the robust-
ness and precision of its numerical conversions and other mathematical
operations. And it pioneers in supporting the use of new C++ features such
as templates, exceptions, and namespace declarations.
To benefit from these ambitions, a user should be aware of numerous
subtleties. To satisfy these ambitions, an implementor must provide for
them. These subtleties are not always addressed in the draft C++ Standard
proper. It is not the primary purpose of a standard to educate implemen-
tors. Nor are many of these subtleties well explained in the piecemeal
descriptions published to date on the C++ library. This book endeavors to
highlight the subtle and make it more obvious.
The pioneering features I mentioned above are not found in traditional
implementations of C++ libraries. An implementation now uses templates
where once "clever" macros had to suffice. The library throws exceptions
where once it returned error codes that were often ignored. And the library
is now wrapped with namespace declarations where once it provided an
Preface xiii
untidy soup of names. Little or no prior art exists for these new features.
Hence, even the most experienced C++ programmers need guidance in
understanding library templates, exceptions, and namespace declarations.
Particular attention is given here to these topics.
designing Still another purpose of this book is to teach programmers how to design
libraries and implement class libraries in general. By its very nature, the library
provided with a programming language is a mixed bag. An implementor
needs a broad spectrum of skills to deal with the varied contents of the bag.
It is not enough to be a competent class designer or numerical analyst, or
to be skilled in manipulating character strings efficientl~ or to be knowl-
edgeable in the ways of operating system interfacing. Writing a library
demands all these skills and more.
Good books have been written on how to write mathematical functions.
Other books present specialized class libraries for a variety of purposes.
They show you how to use the library presented. Some may even justify
many of the design choices for the particular library in question. Few, if any,
endeavor to teach the skills required for library building in general. Those
that do tend to have a narrower focus of applicability than this book.
reusability A number of books present general principles for designing and imple-
menting software. The disciplines they present have names such as struc-
tured analysis, structured design, object-oriented design, and object-ori-
ented programming. Most examples in these books consider only programs
written for a custom application. Nevertheless, the principles and disci-
plines apply equally well to the writing of reusable libraries.
The goal of reusability simply raises the stakes. If a library class is not
highly cohesive, in the structured-design sense, then it is less likely to find
new uses. If it does not have low coupling to other classes, in the same sense,
it is harder to use. Similarly, a collection of classes must hide implementa-
tion details and provide complete functionality. Otherwise, it fails at imple-
menting reusable data abstractions, in the best sense of object-oriented
design and programming.
So the final purpose of this book is to address the design and implemen-
tation issues peculiar to C++ library building. The design of the Standard
C++ library is still amazingly fluid. Nevertheless, the standardized existing
practice shown here is a good design in many ways and worthy of discus-
sion. Implementations of the Standard C++ library can vary even while
conforming to the draft C++ Standard. Any number of choices are strongly
dictated by general principles, such as correctness and maintainability.
Other choices are dictated by priorities peculiar to a project, such as the
need for very high performance, portabilit~ or small size. These choices
and principles are also worthy of discussion.
structure The book is structured much like the Standard C++ library itself. Thirty-
of this eight headers declare or define all the names in the library. Eighteen of those
book are inherited from the Standard C library and Amendment 1 to the C
Standard. A single chapter discusses how the Standard C library changes
xiv Preface
to meet the needs of C++. For the rest, a separate chapter covers each
header. I discuss them in their order of presentation within the Informal
Review Draft.
Most of the headers have reasonably cohesive contents. That makes fOf
reasonably cohesive discussions. One or two, however, are catchalls. Their
corresponding chapters are perforce wider ranging. And many headers
cooperate to implement the extensive "iostreams" facili~ Their descrip-
tions are necessarily intertwined.
I include in each chapter excerpts from relevant portions of the Informal
Review Draft. The excerpts supplement the narrative description of how
each portion of the library is customarily used. They also help make this
book a more complete reference (that is nevertheless more readable than
the Informal Review Draft alone). I warn of any future changes to that
portion of the draft, either already adopted, imminent, or at least very likely
to OCCUf. I also show all C++ code needed to implement that portion and to
test the implementation.
Each chapter ends with a set of exercises. In a university course based
on this book, the exercises can serve as homework problems. Many of them
are simple exercises in using the libraI)', or in code rewriting. They drive
home a point or illustrate reasonable variations in implementation. The
more ambitious exercises are labeled as such. They can serve as a basis fOf
more extended projects. The independent reader can use the exercises as
stimulus for further thought.
The Code
The code presented in this book has been tested with C++ compilers
from Borland, Microsoft, Sun Microsystems, Metrowerks, and the Edison
Design Group. It has passed the widely used Plum Hall Validation Suite
tests. While I have taken pains to minimize errors, I cannot guarantee that
none remain. Please note the disclaimer on the copyright page.
Please note also that the code in this book is protected by copyright. It
has not been placed in the public domain. Nor is it shareware. It is not
protected by a "copyleft" agreement, like code distributed by the Free
Software Foundation (Project GNU). I retain all rights.
fair use You are welcome to transcribe the code to machine-readable form for
your personal use. You can purchase the code in machine-readable from
Plum Hall Inc. in Kamuela, Hawaii. In either case, what you do with the
code is limited by the "fair use" provisions of copyright law. Fair use does
not permit you to distribute copies of the code, either hard copy or machine-
readable, either free or for a fee.
Having said that, I do permit one important usage that goes well beyond
fair use. You can compile portions of the library and link the resultant
binary object modules with your own code to form an executable file. I
Preface xv
hereby permit you to distribute unlimited copies of such an executable file.
I ask no royalty on any such copies. I do, however, require that you
document the presence of tIle libral)T, whatever amount you use, either
modified or unmodified. Please include somewhere in the executable file
the following sequence of characters: Portions of this work are de-
rived from The Standard c++ Library, copyright (c) 1995 by
P.J. Plauger, published by Prentice-Hall, and are used with
permission. The same message should appear prominentl)', and in an
appropriate place, on any documentation that you distribute with the
executable image. If you omit either message, you infringe the copyright.
(These are the same terms on which I have offered the Standard C library
for several years now.)
licensing You can also obtain permission to do more. You can distribute the entire
library in the form of binary object modules. You can make the source code
available on a network to a group of co-workers. You can even distribute
copies of the source files from this book, either modified or unmodified.
You can, in short, incorporate the library into a product that lets people use
it to make executable programs. To do so, however, requires a license. You
pay a fee for the license. Contact Plum Hall Inc. for licensing terms and for
on-going support of the library.
Despite the mercenary tone of these paragraphs, my goal is not simply
to flog a commercial product. I believe this is a reasonable basis for the
Standard C++ library: While I have contributed little to the technical con-
tent, I have worked hard to help refine its expression and specification. I
want to raise public awareness of this, the first really universal library for
writing C++ programs. I wrote this implementation, and this book, to help
achieve that important goal.
Acknowledgments
The Library Working Group within both X3J16 and WG21 has worked
for years to bring the draft Standard C++ library to its present useful state.
I happily acknowledge the contributions of the many people who have
served in the Library Working Group, and to two people in particular. Mike
Vilot has chaired that group from the outset. Jerry Schwarz has borne the
brunt of the effort needed to tum the specification for his popular iostreams
package into an international standard. More than anything, this book is
intended as a tribute to the extensive efforts of the Library Working Group.
Paul Becker, my Publisher at Prentice-Hall, has provided his usual
patient support. He has maintained his enthusiasm for this project even as
it has grown from one year to nearly three. I appreciate his professional
focus, and I'm grateful that he favors quiet encouragement over nagging.
Tom Plum has provided extraordinary support in the development,
testing, and packaging of the code in this book. He has devoted many hours
to porting the code to popular platforms, to automating builds and tests,
xvi Preface
and to coordinating the efforts of beta testers. He has also contrived
solutions to any number of nasty design problems, along the wa)T. Michael
Marcotty of Metrowerks has also been most helpful in reporting both bugs
and fixes for them.
Tim Prince kindly provided the coefficients needed for the revised
Standard C math libraI)', in all three floating-point precisions. His contri-
bution is not directly visible in this book, but it serves as a vital underpin-
ning to the complex arithmetic classes. Also invisible, but most helpful,
have been the excellent math tests from Cody and Waite (C&W80). They
were instrumental in proving in the math functions.
I have benefited from several discussions with Dan Saks and Chuck
Allison. Their greater experience with C++ has often shown me graceful
solutions to seemingly intractable problems. I have enjoyed the illuminat-
ing perspectives of Beman Dawes and Pete Becker, also active participants
in the Library Working Group, and of Tony Hansen, Andy Koenig, and
Bjame Stroustrup. Knowingly or not, all these folks have influenced the
presentation in this book, I think to advantage. (But I respect their right to
disagree, or to disclaim any intentional influence.)
Much of the material presented here first appeared in monthly install-
ments in The C Users Journal. I thank Robert Ward, as publisher, for enabling
me to blend so easily dual careers as magazine writer/ editor and author of
textbooks. I am likewise grateful to John Levine, editor and publisher of
The Journal of C Language Translation. Some of my quarterly contributions to
that publication have also found their way into this book.
Those who reviewed this manuscript for gaffes, typos, and infelicities
include Chuck Allison and Tom Plum. For making this a more readable
book, they deserve your thanks at least as much as mine.
Finally, I acknowledge the wonderfully supportive environment pro-
vided by my family. My son, Geoffre)T, assisted with the layout and typo-
graphic design of this book. My wife, Tana, was ever ready to off-load any
tiresome administrative chores. And far more important, both provided
ample reason and reward for doing the bits that weren't always fun.
~J. Plauger
Concord, Massachusetts
Chapter 0: Introduction
Background
A library is a collection of program components that can be reused in
many programs. Most programming languages include some form of
librar)T. The programming language C++ is no exception. It inherits a great
number of useful functions from the Standard C librar)', right from the start.
These functions help you classify characters, manipulate character strings,
read input, and write output - to name just a few categories of services.
overloading To these can be added even more functions that profit from the special
and operators notation of c++. You can overload a function name, for example:
float_complex pow(float_complex, float_complex);
float_complex pow(float_complex, float);
float_complex pow(float_complex, int);
float_complex pow(float, float_complex);
Different versions of a function get called depending on the types and
number of arguments in a particular function call. For the object x of type
float_complex, pow(X, X) and pow(X, 3. OF) call different functions.
You can overload operators as well, as in:
float_complex operator+(float_camplex, float_complex);
float_complex operator+(float_camplex, complex);
float_complex operator+(float, float_complex);
Different versions of an operator function get called depending on the types
of operands for that operator in a particular expression. So x + x and x +
3 • OF also call different functions.
classes You can also define classes. A class encapsulates all the data needed to
and model a particular instance of some abstract entit)T, along with all the
templates functions you need to manipulate that data. Properly designed, a class is
likely to be a more reusable program component than a function. Finally,
you can declare template functions and telnplate classes. A template has one
or more parameters, usually including type parameters. You can, for exam-
ple, instantiate different versions of a template that perform essentially the
same services for operands of different types.
The C++ library described in this book adds an assortment of capabilities
to those it inherits from Standard C. These capabilities include:
2 Chapter 0
• an iostreams package that overloads the operators << and » for conven-
ient and type-safe input and output
• a string package that overloads several operators for convenient ma-
nipulation of text strings
• a complex package that overloads most of the arithmetic operators for
complex arithmetic, based on a variety of floating-point data types
As you might expect, these packages and their brethren make use of
function and operator overloading, class definitions, and templates.
What you might not expect, however, is the particular choice of capabili-
ties described here. C++ is rightly praised as a boon to writing reusable
code. A well crafted library of classes can model a given application domain
to advantage for a broad range of related programs. Classes provide
uniformi~ centralization of concepts, and enforced semantics across pro-
ject boundaries. But this book is not about designing such classes, at least
not exactly:
The question addressed here is somewhat different - what functions
and classes are sufficiently useful that they should be available to all C++
programs? That constitutes library design in the same spirit as program-
ming-language design. In that spirit, this book is a sequel to my earlier
opus, The Standard C Library (Pla92). There, I described the library man-
dated by ISO/IEC 9899:1990, the international standard for the program-
ming language C (AN889 and 18090). By the inheritance I mentioned
earlier, that means I have already described a large chunk of the Standard
C++ library: The purpose of this book is to describe everything else needed
to make the Standard C++ libral)T, at least as of a given point in time.
(Amendment 1 to the C Standard, now in the final stages of ISO balloting,
adds a bunch of C functions for manipulating very large character sets
(18094). I describe them here only in passing.)
What follows is a number of definitions that pertain to the business of
describing, using, and implementing a library in C++. Much of this chapter
is cribbed heavil)T, and unapologeticall)T, from the corresponding introduc-
tion to The Standard C Library. C++ is built atop C in many senses. Both
languages are best described in terms of a common vocabulary:
a few You must declare a typical function before you use it in a program. The
definitions only safe way to do so is to incorporate into the program a header that
declares all the library functions and classes in a given category: A header
can also define any associated type definitions and macros. A header is as
much a part of the library as the functions themselves. Most often, a header
is a text file just like the file you write to make a program.
You use the #include directive in a C++ source file to make a header
part of the translation unit. For example, the header <stdio. h> declares the
Standard C functions that perform input and output. A program that prints
a simple message with the function printf looks much like the correspond-
ing C program. It consists of the single C++ source file:
Introduction 3
II a simple C-ish test program
#include <stdio.h>
int main{void)
{ II say hello
printf ("Hello\n");
return CO);
}
Even better, you can use the header <iostream> to make use of the
iostreams package in an equally simple C++ source file:
II a simple C++-ish test program
#include <iostream>
int main{void)
{ II say hello
cout « "Hello" « endl;
return CO);
}
strlen is a small function, one fairly easy to write. It is also fairly easy to
write incorrectly in many small ways. strlen is widely used. You might
want to provide a special version tuned to a given computer architecture.
But you don't have to. This version is correct, portable, and reasonably
efficient.
Other contemporary languages cannot be used to write significant por-
tions of their own libraries. You cannot, for example, write the Pascal library
function writeln in portable Pascal. By contrast, you can write the equiva-
lent C or C++ library function printf in portable C++. The comparison is
a bit unfair because c++ type checking is weaker in this case, at least for
varying length argument lists. (That's partly why the Standard C++ library
adds iostreams.) Nevertheless, the underlying point is significant - the
C++ library has been expressible from its earliest beginnings almost com-
pletely in C++.
nonportable Sometimes the code for a library function cannot be written in portable
code C++. The code you write in C++ may work for a large class of computer
architectures, but not all. In such a case, the important thing is to document
clearly the nonportable portions tllat may have to change. You should also
isolate nonportable code as much as possible. Even nonportable C++ code
is easier to write, debug, and maintain than assembly lang.uage. You write
assembly language only where it is unavoidable. Those places are few and
far between in the Standard C++ librar)'.
Of particular help is the existence of the Standard C libral)T. Most places
where nonportable code is required are already encapsulated by functions
from the older library. Thus, the added code presented here for the Standard
c++ library can usually avoid portability problems simply by calling
functions from the Standard C library as necessary.
Introduction 5
This book shows you how to use the Standard C++ library presented
here. Along the way, it also shows you how to write this library in C++.
That can help you understand how the library works. And it illustrates
many aspects of designing and writing a nontrivial library in C++.
17 Library
17.1 Introduction
introduction A C++ implementation provides a Standard C++ library that defines various
entities: types, macros, objects, and functions. Each of these entities is declared
or defined (as appropriate) in a headel; whose contents are made available to a
translation unit when it contains the appropriate #include preprocessing
directive. 48 ) Objects and functions defined in the library and required by a c++
program are included in the program prior to program startup.
17.1.1 Standard C library
Standard C This International Standard includes by reference clause 7 of the C Standard and
library clause 4 of Amendment 1 to the C Standard (1.2). The combined library
described in those clauses is hereinafter called the Standard C library. With the
qualifications noted in this subclause 17.1 and in subclause 17.2, the Standard
C library is a subset of the Standard C++ library.
17.1.2 Headers
headers The Standard C++ library provides 39 primary headers, each with a correspond-
ing secondary header, as shown in Table 13:
Table 13 - Library headers
PRIMARY SECONDARY PRIMARY SECONDARY
<all.ns> <all> <bits.ns> <bits>
<cassert.ns> <assert.h> <bitstring.ns> <bitstring>
<cctype.ns> <ctype.h> <defines.ns> <defines>
<cerrno.ns> <errno.h> <dynarray.ns> <dynarray>
<cfloat.ns> <float.h> <exception.ns> <exception>
<ciso646.ns> <iso646.h> <fstream.ns> <fstream>
<climits.ns> <limits.h> <iomanip.ns> <iomanip>
<clocale.ns> <locale.h> <ios.ns> <ios>
<cmath.ns> <math.h> <iostream.ns> <iostream>
<complex.ns> <complex> <istream.ns> <istream>
<csetjmp.ns> <setjmp.h> <new.ns> <new>
<csignal.ns> <signal.h> <ostream.ns> <ostream>
<cstdarg.ns> <stdarg.h> <ptrdynarray.ns> <ptrdynarray>
<cstddef.ns> <stddef.h> <sstream.ns> <sstream>
<cstdio.ns> <stdio.h> <streambuf.ns> <streambuf>
<cstdlib.ns> <stdlib.h> <string.ns> <string>
<cstring.ns> <string.h> <strstream.ns> <strstream>
<ctime.ns> <time.h> <typeinfo.ns> <typeinfo>
<cwchar.ns> <wchar.h> <wstring.ns> <wstring>
<cwctype.ns> <wctype.h>
If the name (enclosed in angle brackets) of a secondary header ends in .h, that
header and its corresponding primary header are associated with the Standard
C library and are called C headers. All other headers are called C++ headers.
If a header is implemented as a source file, the derivation of the file name from
the header name is implementation-defined. If a file has a name equivalent to
the derived file name for one of the above headers, is not provided as part of the
Introduction 7
implementation, and is placed in any of the standard places for a source file to
be included, the behavior is undefined.
<all> The header <all.ns> includes all other primary headers. The header <all>
includes all other secondary headers.
A translation unit may include these headers in any order. Each may be included
more than once, with no effect different from being included exactly once,
except that the effect of including either <cassert .ns> or <assert .h>
depends each time on the lexically current definition of NDEBUG. A translation
unit shall include a header only outside of any external declaration or definition,
and shall include the header lexically before the first reference to any of the
entities it declares or first defines in that translation unit.
Certain types, macros, and namespace aliases are defined in more than one
header. For such an entity, a second or subsequent header that also defines it may
be included after the header that provides its initial definition.
None of the C headers includes any of the other headers, except that each
secondary C header includes its corresponding primary C header. Except for the
headers <all.ns> and <all>, none of the C++ headers includes any of the C
headers. However, any of the C++ headers can include any of the other C++
headers, and must include a C++ header that contains any needed definition. 49 )
17.1.3 Namespaces
namespaces Except for the header <all.ns>, each C++ header whose name has the form
name.ns declares or defines all entities within the namespace iso_stand-
ard_library: :name. 50 )
Except for the header <all>, each C++ header whose name has the form name
includes its corresponding primary header name. ns, followed by the declara-
tion:
using namespace iso_standard_library::name
In addition, the header <new> contains the declarations: 51 )
using iso_standard_library::new::operator delete
using iso_standard_library::new::operator new
Each C header whose name has the form cname.ns declares or defines all
entities within the namespace iso_standard_library: :c: : name.
Each C header whose name has the form name.h includes its corresponding
primary header cname.ns, followed by the declaration
using namespace iso_standard_library::c::name
In addition, for each function or object x declared with extemallinkage in its
corresponding primary header cname.ns, the header name.h contains the
declaration52 )
using iso_standard_library::c::name::X
Descriptions of header contents in this clause name the secondary headers
instead of the primary headers. A statement such as "xis defined or declared in
<ios>" is equivalent to "x is defined or declared by including <ios>, which
includes <ios • ns> to obtain the actual declaration or definition."
17.1.4 Reserved names
reserved A translation unit that includes a header shall not contain any macros that define
names names declared or defined in that header. Nor shall such a translation unit define
macros for names lexically identical to keywords.
Each header defines the namespace iso_standard_library and its alias
std. Each header declares or defines all names listed in its associated subclause.
Each header also optionally declares or defines names which are always reserved
8 Chapter 0
to the implementation for any use and names reserved to the implementation for
use at file scope.
Each name defined as a macro in a header is reserved to the implementation for
any use if the translation unit includes the header. 53)
Certain sets of names and function signatures are reserved whether or not a
translation unit includes a header:
• Each name that begins with an underscore and either an uppercase letter or
another underscore is reserved to the implementation for any use.
• Each name that begins with an underscore is reserved to the implementation
for use as a name with file scope or within the namespace iso_stand-
ard_l ibrary in the ordinary name space.
• Each name declared as an object with external linkage in a header is reserved
to the implementation to designate that library object with external linkage. 54)
• Each global function signature declared with external linkage in a header is
reserved to the implementation to designate that function signature with
extemallinkage. 55 )
• Each name having two consecutive underscores is reserved to the implemen-
tation for use as a name with both extern "C" and extern "C++ linkage. II
Here, the names co, Cl, etc. represent enumerated elements for this particular
enumerated type. All such elements have distinct values.
17.1.5.10.2 Bitmask types
bitmask Several types defined in this clause are bitmask types. Each bitmask type can be
types implemented as an enumerated type that overloads certain operators. The
bitmask type bitmask can be written:
anum secret {
VO = 1 « 0, Vl = 1 « 1, V2 1« 2, V3 1« 3, •..•• };
typedef secret bitmask;
static const bitmask CO(VO);
static const bitmask Cl(Vl);
static const bitmask C2(V2);
static const bitmask C3(V3);
Footnotes:
Footnotes 48) A header is not necessarily a source file, nor are the sequences delimited by
< and> in header names necessarily valid source file names.
49) Including anyone of the C++ headers can introduce all of the C++ headers
into a translation unit, or just the one that is named in the #include
preprocessing directive.
50) Macro definitions nevertheless occupy a disjoint name space.
51) Including the header <new> permits references of the form: : operator
new.
52) Including the C secondary header permits references of the form : : X.
53) It is not permissible to remove a library macro definition by using the
#undef directive.
54) The list of such reserved names includes ermo, declared or defined in
<errno.h>.
55) The list of such reserved function signatures with external linkage includes
setjmp (jmp_buf), declared or defined in <setjmp .h>, and
va_end(va_list), declared or defined in <stdarg. h>.
56) The function signatures declared in <wchar. h> and <wctype. h> are
always reserved, notwithstanding the restrictions imposed in subclause
4.5.1 of Amendment 1 to the C Standard for their corresponding secondary
headers.
57) The only reliable way to declare an object or function signature from the
Standard C library is by including the header that declares it, notwithstand-
ing the latitude granted in subclause 7.1.7 of the C Standard.
58) A global function cannot be declared by the implementation as taking
additional default arguments. Also, the use of "masking macros" for func-
tion signatures declared in C headers is disallowed, notwithstanding the
latitude granted in subclause 7.1.7 of the C Standard. The use of a masking
macro can often be replaced by defining the function signature as inline.
59) A function can catch an exception not documented in this clause provided
it rethrows the exception.
60) The function signatures, all declared in <new>, are operator de-
lete(void*), operator delete[] (void*) , operator new (
size_t) , and operator new [] (size_t).
61) Hence, taking the address of a member function has an unspecified type.
The same latitude does not extend to the implementation of virtual or global
functions, however.
14 Chapter 0
62) A valid C++ program always calls the expected library member function,
or one with equivalent behavior. An implementation may also define
additional member functions that would otherwise not be called by a valid
C++ program.
63) A valid C++ program always calls the expected library global function. An
implementation may also define additional global functions that would
otherwise not be called by a valid C++ program.
64) An implicit exception to this rule are types described as synonyms for basic
integral types, such as size_t and streamoff.
65) Many of the objects manipulated by function signatures declared in
<string .h> are character sequences or NTBSs. The size of some of these
character sequences is limited by a length value, maintained separately from
the character sequence.
66) A string literal, such as "abc", is a static NTBS.
67) An NTBS that contains characters only from the basic execution character
set is also an NTMBS. Each multibyte character then consists of a single
byte.
68) Many of the objects manipulated by function signatures declared in
<wchar •h> are wide-character sequences or NTWCSs.
69) A wide string literal, such as L"abc", is a static NTWCS.
Future Directions
This "front matter" to clause 17 lays down ground rules that cover the
entire library portion of the draft C++ Standard. While there is continuing
restatement or rearrangement of this material, many of the ground rules
now appear to be widely accepted. A few areas, however, are still somewhat
volatile. Some have, in fact, already changed.
headers To begin with, the header <all> has been deleted. Also, you need no
longer worry about the distinction between "primary" and "secondary"
headers (17.1.2). The Committee has already scrubbed all those files whose
names end in .ns. The nearest thing to a replacement is a set of new C++
headers with names like <cassert> (corresponding to <assert.h»,
<cctype> (corresponding to <ctype.h», and so on for all 18 C headers.
You might not recognize three of these C headers, by the wa~ Amendment
1 to the C Standard introduces <iso646 .h>, <wchar .h>, and <wctype .h>
(15094).
The relationship is much as before. The header <cctype>, for example,
declares all its functions inside a namespace declaration (17.1.3). A namespace
declaration encloses a sequence of declarations much like a class does, but
without all the other semantics implied by a class declaration. The library
namespace declaration nesting has also been radically simplified. Now, each
C++ header simply contributes to the single library namespace declaration
std, as in this skeleton for <cctype>:
II possible skeleton for <cctype>
namespace std {
int isalnum(int);
int isalpha(int);
I I and so on
};
Introduction 15
If you include <cctype> in a translation unit, you can refer to the
functions by their qualified names, such as std: : isalnum, std: : isalpha,
and so on. With this approach, you don't have to worry about library names
colliding with any names your program defines, other than macro names.
Or you can "hoist" names into file scope by writing (at file scope):
#include <cctype>
using std::isdigit;
From that point on, the name isdigit behaves as in the past. You can use
it either qualified or unqualified. And, of course, it can also collide with
names your program defines.
The corresponding C header does this hoisting for you. The skeleton for
<ctype.h> now looks like:
II possible skeleton for <ctype.h>
#include <cctype>
using std::isalnum;
using std::isalpha;
I I and so on
The primary change here is the omission of the using declaration for
hoisting all names from the (now nonexistent) nested namespace declaration
peculiar to the header.
whither Old hands at C++ will note the absence of traditional headers such as
<iostream.h> <iostream.h> and <new.h>. (See, for example, Tea93.) They have so far
been intentionally omitted from the draft C++ Standard. If they reappear,
I suspect they will behave much like the C headers, to ease migration of
existing code. If they do not, you can be sure that vendors of C++ translators
will supply them anyway:
It is not clear to me at this writing whether a header such as <iostream>
will hoist its names. I doubt that even the most ardent proponents of
namespace declarations will happily write std: :cout repeatedly through-
out a program. But a header that hoists may well be added with a different
name (such as the venerable <iostream. h>, in this particular case).
The code in this book assumes, as the Informal Review Draft states, that
such hoisting does indeed occur. It may have been more prudent to protect
the code from changes in header conventions. Certainl)T, they have been a
volatile and inventive area for some time now. But I chose not to add such
protection. In fact, I provide only the C++ headers with no file extension,
such as <iostream>. And these contain no namespace or using declarations.
(I currently have access to no translators, commercial or otherwise, that
even implement namespace declarations.) As a result, the code may need
widespread changes when the C++ headers eventually settle down.
user-defined Some sentiment has also been expressed for reducing the bite taken out
macros of the library namespace by user-defined macros (17.1.4). Various schemes
have been proposed for inhibiting or prohibiting user-defined macro ex-
pansion within library headers. One benefit of such a change would be that
implementors could more easily add member functions to library classes.
Old hands at C++ will inevitably note, and mourn, the absence of certain
16 Chapter 0
features intentionally omitted during standardization. (ios: : fd () is one
example, for those of you in the know.) Vendors dare not add them back if
their llames can be masked by user-defined macros. The headers will
simply translate improperly.
exceptions The exception-handling machinery has been overhauled repeatedly, and
bids fair to be modified still furtller. The class name xalloe (17.1.5.3), for
example, is now bad_alloe. And the convention of throwing all library
exceptions by calling the member function raise ( ) for the exception object
is under review. I discuss this matter at greater length in Chapter 3:
<exception>, particularly under Future Directions.
optional For backward compatibility with existing practice, the Informal Review
members Draft permits several member functions within library classes to be over-
loaded with "old-fashioned" versions of three enumerated types. These
older types are ios:: io_state (replaced by ios:: iostate), ios::
open_mode (replaced by ios: :openmode), and ios: :seek_dir (replaced
by ios: : seekdir). Member functions that use the older types are branded
"optional members" here (17.1.5.6). You will see more of these beginning
with Chapter 6: <ios>.
The Committee has already decided, however, to describe such addi-
tions in a non-normative annex as "deprecated features." I suspect that the
practical effect of tilis change of emphasis will be minimal. Wise vendors
will continue to supply the "old-fashioned" member functions because the
draft C++ Standard permits the practice. More important, they will do so
because users will demand tIle backward compatibilit~
You can't use this mechanism for the C header <assert. h>, by the way.
Its behavior is controlled by the macro name NDEBUG that the programmer
can choose to define. Each time the program includes this header, the
header turns the assert macro off or on, depending upon whether or not
NDEBUG has a macro definition at that point in the translation unit.
mutual Maintaining mutual independence among the C++ headers is easier
independence than in C because the C++ headers can include each other as they see fit.
"Mutual independence" merely requires that the C++ headers do all the
necessary including, rather than leave the job to the programmer. Problems
24 Chapter 0
occur only when a C++ header would dearly love to include a C header.
The draft c++ Standard mitigates this problem by introducing the standard
header <defines>. (See Chapter 2: <defines>.) Among other things, it
defines several types also defined widely throughout C headers, the most
popular being size_to It is the type that results from applying the sizeof
operator, and it occurs frequently as a function parameter type or return
value type.
But this service causes a potential clash between <defines> and various
C headers. A program must be able to include two different headers that
define the same name without causing an error. The type definition size_t
is a classic example. In Standard C, the canonical way to protect against
multiple definitions of this type is with another macro guard:
#ifndef _S:J:ZET
#define _S:J:ZET
typedef unsigned int size_t;
#endif
This technique extends easily to C++ headers as well (but see the further
discussion on page 47.)
benign The macro EOP is another example. Both the C header <stdio.h> and
redefinition the C++ header <streambuf> define it. BOP signals end-of-file when ex-
tracting characters from a stream, or an invalid return value. One way to
define this macro is:
#define EOP (-1)
It does no harm to include multiple instances of this macro definition in a
translation unit. Both Standard C and the draft C++ Standard permit benign
redefinition of a macro. Two definitions for the same macro name must have
the same sequence of tokens. They can differ only in the white space (in this
case, spaces and horizontal tabs) between tokens. You need not protect
against including two definitions that match in this sense.
You do have to provide the same definition for several entities in
multiple places, however. That is an annoying maintenance problem. Two
solutions are:
• Write the same definition in multiple places. Be prepared to hunt down
all occurrences if the definition changes.
• Place the definition in a separate header file. Give the file a name that
should not collide with file names created by the programmer. Include
the file in each header that requires it.
<yxvals.h> I chose the second solution (most of the time) because it simplifies adapting
the library to different implementations of C++. The header <yxvals.h>,
peculiar to the implementation of the Standard C++ library in this book, is
a catchall. It holds configuration-specific macros and type definitions, as
well as definitions that must be shared with C headers. (The analogous
header in my implementation of the Standard C library is <yvals.h».
Appendix A: Interfaces summarizes the various contributions to this "in-
ternal" header.
Introduction 25
A related issue arises with the classes stdiobuf, istdiostream, and
ostdiostream, described in Chapter 13: <f stream>. All have constructors
that take a parameter of type pointer to FiLE. But that type is not defined in
this particular header. It is defined only in the C header <stdio.h>. How
can this be?
synonyms The answer is simple, if a bit subtle. The header <fstream> must contain
a synonym for the type FiLE. The synonym has a name from the set reserved
for macros. That's all that's needed within the standard header to declare
each of the three constructors. (Of course, the implementor faces the same
problems replicating either visible definitions or synonyms in multiple
headers.)
It's rather difficult for you as a programmer to use any of these functions
without a definition for FiLE. (It can be done, but it's probably not good
style.) That means you probably want to include the header <stdio. h> any
time you make use of any of these functions. Still, it's the programmer's
problem. The implementation need not (and must not) drag in <stdio. h>
every time the program includes <streambuf>.
headers at The final property of standard headers is primarily for the benefit of
file level implementors. The programmer must include a standard header only
where a file-level declaration is permitted. That means the #include
directive must not occur anywhere inside another declaration. Most stand-
ard headers must contain one or more external declarations. These are
permissible only in certain contexts. Without the caveat, many standard
headers would be impossible to write as ordinary C++ source files.
Exercises
Exercise O. 1 Which of the following are good reasons for including a class in a library?
• The class is widely used.
• Performance of the member functions in the class can be improved
dramatically by special optimizations.
• The class is easy to write and can be written several different ways.
• The class is hard to write correctl}T.
• Writing the class poses several interesting challenges.
• The class proved very useful in a past application.
• The class performs a number of services that are loosely related.
Exercise 0.2 Write a (correct) program that contains the line:
x: x: :x.x = x(S);
Describe the five distinct uses of x. Can you make a case for using any two
of these meanings at once in a sensible program?
Exercise 0.3 A library class x declares a member function y. What happens if the
program defines a macro named y before it includes the header that defines
the class x?
Exercise 0.4 What happens if the program includes the header that defines x, as above,
then defines a macro named y after it includes the header?
Exercise 0.5 If any standard header can include any other, what style must you adopt
to avoid problems?
Exercise 0.6 [Harder] If a standard header can define arbitrary names, what must a
programmer do to ensure that a large program runs correctly when moved
from another implementation?
Exercise 0.7 [Very hard] Describe an implementation that tolerates arbitrary names,
even keywords, being masked by macros when you include standard
headers.
Exercise 0.8 [Very hard] Describe an implementation that tolerates standard headers
being included inside class declarations, function definitions, or at any
arbitrary place within a source file.
Chapter 1: Standard C Library
Background
An important reason for the success of c++ is that it's built atop C. That
confers several immediate advantages:
• C++ inherits C's well thought out technology for basic types, expression
evaluation, and flow of control.
• C++ profits from C's popularity and portability.
• C++ programs can make use of the extensive Standard C library:
And herein lies an interesting irony. For it is the limitations of C that inspired
many of the features of C++. Just as C made C++ possible, it also made it
arguably necessary to many people.
class One of the major advantages of C++ over C is the ability to write class
libraries libraries instead of function libraries. A class encapsulates much more than
just a type definition and a handful of related functions. It can enforce
information hiding and proper protocols for using member functions. A
well designed class, or template class, or set of classes, is bound to be more
reusable than the equivalent collection of functions.
Nevertheless, the Standard C library endures as an important adjunct to
C++. It has not been displaced by a superior set of classes. (The iostreams
package does replace much of what's in < stdio. h>, but not all.) If anything,
I believe that the presence of such a rich function library has inhibited, or
at least delayed, the growth of the kind of class library that many C++
programmers would prefer. As is so often the case, that which is good enough
wins out over that which is arguably the best.
Footnotes:
Footnotes 70) The header <stdlib.h>, for example, makes all declarations and defini-
tions available in the global name space, much as in the C Standard. The
header <cstdl ib. ns> provides the same declarations and definitions
within the namespace iso_standard_l ibrary: : c: : s tdlib.
71) Possible definitions include 0 and OL, but not (void*) O.
72) A function is called for every time it is registered.
73) Automatic objects are all destroyed in a program whose function main
contains no automatic objects and executes the call to exi t. Control can
be transferred directly to such a main by throwing an exception that is
caught in main.
74) Any C streams associated with cin, cout, etc. are flushed and closed when
static objects are destroyed in the previous phase.
Future Directions
I believe most of the tough decisions have been made in the area of
compatibility between the Standard C and Standard C++ libraries. The
toughest decision was not to try to "fix" the Standard C library: Maintaining
backward compatibility with widespread C practice has an obvious payoff.
Outlawing irregular or inconvenient features may make for a prettier
libra!}', but raises the cost of migrating from C to C++. So the Committee
has mandated changes in the Standard C library only when it felt a pressing
need. I can think of only a few open issues:
locales The Committee has voted in extensive additions to the machinery
associated with C locales. These additions encapsulate locales within "lo-
cale objects," permit different input and output streams to operate in
different locales, and extend the set of locale-specific operations. Superfi-
ciall)T, nothing need change in the C header <locale. h>, but in practice, C
locales may in future be implemented atop C++ locale objects. Or both may
be implemented atop a common foundation.
math The C Standard supplies a number of common math functions, with
functions names like exp, sin, and sqrt, that operate on double operands and produce
double results. It also reserves names for a parallel set of float functions (such
as expf, sinf, and sqrtf), and for a similar set of long double functions (such
as expl, sinl, and sqrtl). But these latter two groups are not required by
the Standard C libra~
The draft C++ Standard defines complex arithmetic classes in all three
precisions. (See Chapter 21: <complex>.) They require these additional
math functions implicitl)', whether or not their presence is explicitly called
Standard C Library 33
out. Thus, it might be a kindness to programmers to spell out such a
requirement. That way, the functions will assuredly be present with stand-
ard names, and hence more widely available.
<stdarg.h> The macros defined in <stdarg.h> barely work, in most implementa-
tions of Standard C. To ask them to deal with reference parameters, and
references to objects of type va_list, is probably pushing things a bit too
far. The Informal Review Draft doesn't address this subject, but there has
been some discussion within the Committee about limiting what the pro-
grammer can expect of these macros. And a typical implementation of C++
will almost certainly fail if the macros are pushed too hard. I'm sure the
draft C++ Standard will eventually restrict what's expected of <stdarg • h>.
Exercises
Exercise 1. 1 C headers traditionally generate inline code for a library function by
masking its declaration with a macro, as in:
#define isdigit(c) (_Ctype[(int)(c)] & _DI)
Write a valid C++ code sequence that fails to translate properly in the
presence of such a macro definition.
Exercise 1.2 How can you alter a C header so that it still generates inline code for a
library function in the absence of a masking macro?
Exercise 1.3 Write a valid (portable) C++ code sequence that can detect whether the
header <string. h> has the modified function declarations required by the
draft C++ Standard.
Exercise 1.4 A C++ version of the header <math. h> could, in principle, overload existing
function names, as in:
double sin(double);
float sin(float);
long double sin(long double);
rather than use the distinct names reserved by the C Standard (such as sinf
and sinl). Is there any reason why this might not be a good idea? (Hint:
Does any Standard C program change meaning when translated as C++
with such a revised header?)
Exercise 1.5 [Harder] Revise a C++ implementation of the C header <stdarg.h> to
work properly with:
• a scalar argument in a varying length list that changes representation
when subject to default argument promotion (integer types smaller than
int, or the type float)
• an aggregate argument in a varying length list other than a "plain old
data structure"
• references to objects of type va_list
• a reference parmN parameter (the parameter argument to va_start)
Exercise 1.6 [Very hard] Write the specification for a true null pointer constant in C++.
Describe the properties it should have:
• when assigned to a pointer object
• when assigned to an arithmetic object
• when type cast to all other scalar types
• when used as the actual argument on a call to an overloaded function
• when used as the actual argument in a varying length argument list
40
Chapter 2: <defines>
Background
The header <defines> is an invention of the Committee. It was added
to serve as a repository for several definitions needed throughout the
Standard C++ libraty Writing, and describing, several C++ headers proved
to be a nuisance without these definitions. On the other hand, no existing
header seemed appropriate as a parking place for anyone of the items
defined here. Thus, the header <defines> has a history and raison d'etre
similar to the C header <stddef .h>.
Footnotes:
Footnotes 75) The extra value is denoted by the macro WEOF, defined in <wchar .h>. It
is permissible for WEOF to be in the range of values representable by
wchar_t.
Future Directions
<stddef> This header has already been renamed <stddef>.
The header <defines> is a catchall for definitions needed throughout
the Standard c++ library. Hence, it is likely to accrete more material as time
goes on. The type definitions inherited from Standard C are least likely to
change, but the others are all invented to serve the needs of C++. They are
being refined over time.
fvoid_t Some people have objected to the type fvoid_t, for example, as being
too generalized and too primitive. Earlier editions of the draft C++ Stand-
ard used specialized versions of this type for each of its applications (such
as set_terminate and set_new_handler). The Committee has already
restored these specializations, and eliminated fvoid_t as a communal type
in the bargain.
<defines> 43
ptrdiff_t The type ptrdiff_t is not actually used in any of the c++ headers. In
fact, it's not actually used for much of anything. It may well be removed
from this header.
capacity The enumeration capacity is used to disambiguate certain construc-
tors, as described below. The classes that use this trick are all serious
candidates for major changes, as I describe later in this book. (See, for
example, Chapter 18: <bitstring>.) Also, the language weakness that
leads to these ambiguities may very well be patched in the near future. If
either or both of these events occurs, the need for capacity will disappear.
};
introduce implicit conversions to class X, in many contexts, from objects of
classwand from all sorts of arithmetic expressions. The latter include values
as diverse as 0, 'X', and even 3.2. It is much too easy to tum a typo-
graphical error into a valid expression, under such circumstances. It is also
very easy to introduce surprising conversion ambiguities, when two or
more related classes pennit conversions from (almost) the same scalar
types.
One way to avoid the problem is to tum single-parameter constructors
into two-parameter constructors. In many cases within the Standard C++
libra!)', that second parameter has type capacity. (See Chapter 15:
<string>, Chapter 16: <wstring>, Chapter 19: <dynarray>, and Chapter
20: <ptrdynarray>.) The value of the enumeration constant further serves
to specify how to interpret the first argument:
default_size • default_size - treat the first argument as the initial size of the newly
constructed object, initializing its elements to zero (for a scalar object),
or a default constructor (for a class object).
reserve - reserve - treat the first argument as the optimum reserve capacity to
set aside for the newly constructed object, but give it an actual initial size
of zero.
I characterize this approach (perhaps unfairly) as something of a kludge
because it solves the underlying problem of unwanted conversions rather
indirectly. For now, however, it's one of the few palatable options available.
46 Chapter 2
Figure 2.1: II defines standard header
#ifndef _DEFINES_
defines
#define _DEFINES_
#ifndef _YXVALS_
#include <yxvals.h>
#endif
II type definitions
typedef void fvoid_t();
#ifndef _PTRDIFFT
#define _PTRDIFFT
typedef _Ptrdifft ptrdiff_t;
#endif
#ifndef _SIZET
#define _SIZET
typedef _Sizet size_t;
#endif
#ifndef _WCHART
#define _WCHART
typedef _Wchart wchar_t;
#endif
#ifndef _WINTT
#define _WINTT
typedef _wintt wint_t;
#endif
enum capacity {default_size, reserve};
II constants
const size_t NPOS =
(size_t)(-l);
II functions
void _Nomemory();
#endif o
NPOS NPOS is the constant value (s ize_t) (-1), the largest value representable
as type size_to Hence, it serves as an effective code either for:
• This size is too huge to be valid.
• This argument value isn't a size - do something else to determine it.
Several library classes describe objects that can hold a varying number of
elements. They all use NPOS with one or both of these special meanings.
(See Chapter 15: <string>, Chapter 16: <wstring>, Chapter 18: <bit-
string>, Chapter 19: <dynarray>, and Chapter 20: <ptrdynarray>.)
Yes, it is indeed possible to declare a repetition count of NPOS in some
declarations. This convention rules out those largest-possible repetition
counts, in the interest of providing a handy code that can be copied about
as just another value of type size_to The Committee felt it was a small
sacrifice to lower the upper limit for a repetition count to NPOS - 1, for a
handful of library classes at least. I'm inclined to agree.
Most of the time, you'll find that NPOS is the default value for an
argument. You'll probably have few occasions to actually write this symbol
in your code. But you should be comfortable with both potential meanings
for NPOS, and learn to recognize which applies in each case.
<defines> 47
Testing <defines>
tdefines. c Figure 2.2 shows the file tdefines. c. It is fairly simple, since the header
is rather small and has little to test in the bargain. Mostl)', it checks for the
overt presence of all the definitions. It then prints the sizes of the flexible
types with which it is preoccupied. If all goes well, the program prints
something like:
sizeof (ptrdiff_t) = 4;
sizeof (size_t) = 4;
sizeof (wchar_t) = 2;
sizeof (wint_t) = 4;
SUCCESS testing <defines>
and takes a normal exit.
Exercises
Exercise 2. 1 Rewrite the declarations:
fvoid_t* set_new_handler(fvoid_t* new_nh);
fvoid_t* set_te~inate(fvoid_t* new_th);
fvoid_t* set_unexpected(fvoid_t* new_uh);
so that the address of, sa)', a new handler cannot be registered as a terminate
handler.
Exercise 2.2 What would be the advantage in making size_t a keyword and a distinct
type?
Exercise 2.3 What would be the advantage in making wint_t a keyword and a distinct
type?
Exercise 2.4 Define at least one additional use for type capacity, besides specifying
default_size or reserve.
Exercise 2.5 [Harder] Devise a method that eliminates the need to reserve the value
(size_t) (-1) for NPOS. Revise the declarations in class string to use this
method. (See Chapter 15: <string>.)
Exercise 2.6 [Very hard] Devise a method for distinguishing conversions from pure
constructors in a class declaration.
<defines> 49
Figure 2.2: II test <defines>
#include <assert.h>
tdefines.c #include <limits.h>
#include <defines>
#include <iostream>
II static data
static capacity cap[] = {default_size, reserve};
static char *pc = 0;
static fvoid_t *fv = 0;
int main ( )
{ II test basic workings of defines definitions
ptrdiff_t pdiff =&pc[XNT_MAX] - &pc[O];
wchar_t wc = _L('Z');
wint_t wi = _L('Z');
Background
Exceptions represent a significant departure in C++ from past program-
ming practice in C. Much of what you write in C++ translates one for one
to very similar C code. The rest may get longer winded in C, and a bit harder
to read, but it's still conventional C. Exception handling, howeve~ changes
the underlying model of how functions get called, automatic storage gets
allocated and freed, and control gets passed back to the calling functions.
A compiler can generate reasonably portable C code to handle excep-
tions, but that code can have serious performance problems - even for
programs that don't use exceptions. The very possibility that an exception
can occur in a called function changes how you generate code for the caller.
Alternativel~ a compiler can generate code directly that can't quite be
expressed in C - and face a different set of problems. It may be hard to mix
such C++ code with that generated from C or another programming
language. Perhaps you can see now why C++ vendors have generally been
slow to add this important new feature to the language.
longjmp What makes exception handling important is that it stylizes a common
setjmp operation expressible in C only in a rather dirty fashion. You can think of
exception handling, in fact, as a disciplined use of the notorious functions
setjmp and longjmp, declared in <setjmp.h>. (Strictly speaking, setjmp
is a macro, but I won't not pursue that distraction for now.)
In a C program, you call setjmp at a point to which you expect to "roll
back." The function memorizes enough context to re-establish the roll-back
point, then returns the value zero. A later call to longjmp can occur
anywhere within the same function or a function called from that function,
however deep in the call stack. By unspecified magic, the call stack gets
rolled back and control returns once again from set jmp. The only difference
is, this time you can tell from the nonzero return value that a longjmp call
initiated the return.
That all adds up to a clever bit of machine!}', used to pull off all sorts of
error-recovery logic over the past couple of decades. The only trouble is,
it's too clever by half. Many implementations have trouble determining
how to roll back all the automatic storage properly. The C Standard is
obligingly vague on the subject, making life easier on the implementors at
52 Chapter 3
the expense of being harder on those wishing to write portable and robust
code. Nobody pretends that <setjmp.h> is an elegant piece of design.
constructors In C++, matters are much more critical. That language prides itself on
destructors cradle-to-grave control of objects, particularly nontrivial ones. You are
assured that every object gets constructed exactly once, before anybody can
peek at its stored values. And you are promised with equal fervor that every
object gets destroyed exactly once. Thus, you can allocate and free auxiliary
storage for an object with a discipline that ensures no files are left open, or
no memory gets lost, in the hurly burly of execution.
longjmp sabotages the best efforts of C++ compilers to maintain this
discipline. In rolling back the call stack, the older C function cheerfully
bypasses all those implicit calls to destructors strewn about by the C++
compiler. Promises get broken, files remain open, storage on the heap gets
lost. The draft C++ Standard leaves <setjmp.h> in the library for upward
compatibilit}T. But it discourages the use of these heavy-handed functions
in the neighborhood of "real" C++ code with nontrivial destructors.
throw Enter exceptions. In modem C++, you don't report a nasty error by
catch calling longjmp to roll back to a point established by setjmp. Instead, you
evaluate a throw expression to roll back to a catch clause. The throw expres-
sion names an object whose type matches that expected by the catch clause.
You can even examine the object to get a hint about what caused the
exception. It's kind of like calling a function with a single argument, only
you're not always sure where the function actually resides. And the func-
tion may be further up the call stack instead of one level further down.
Most important of all, none of those destructors get skipped in the
process of rolling back the call stack. If that sounds like a nightmare in
bookkeeping to you, you're absolutely right. Somehow, the executing code
must at all times have a clear notion of what destructors are pending before
control can pass out of a given block or a given function. It must also deal
with exceptions thrown in constructors and destructors, and exceptions
thrown while processing earlier exceptions. Kids, don't try this at horne.
So this fancier machinery is now in the draft C++ Standard. All that
remains is to decide what to do with it. You can get a few hints from other
programming languages. Ada, to name just one, has had exceptions for
over a decade. Their very presence changes how you design certain inter-
faces and how you structure programs that must respond to nasty errors.
The one certainty is that you must develop a style for using exceptions that
fits the language as a whole, then use it consistently:
library That has serious implications for the Standard C++ library. Traditional1}T,
exceptions of course, the library has thrown or caught no exceptions. (There weren't
any such critters to throw!) But it's a poor advertisement for this new
feature if the library itself makes no use of exceptions. Put more strongl)',
the Standard C++ library has a moral obligation to set a good example.
Many programmers will use only the exceptions defined in the library.
<exception> 53
Others will model their own on what they see used by the libraI)T. Thus, the
library is duty bound to set a good example for the children.
Early on, the Committee committed to using exceptions as part of the
error-reporting machinery of the Standard C++ libraI}T. Not everyone is
happy with this decision. Some people object to it because they don't want
to incur the inevitable overheads of exception handling in every program
that touches the Standard C++ library - and that's essentially every
program you write in C++. Others object because of the putative difficulties
of validating a program that throws exceptions. Some projects require that
the software vendors assert that exceptions can never be thrown. So the
decision to use exceptions in the library was not lightly made.
Only recently has the Committee begun to converge on an overall
structure. It still suffers regular name changes and other tweaks. More
changes are doubtless in the works. Still the general approach is likely to
survive reasonably unchanged.
<exception> All the relevant declarations and class definitions for exception handling
xmsg can be had by including the new header <exception>. Within this header
you can find the definition for the base class xmsg, the mother of all
exceptions thrown by the libraI)T. (Yes, that name has already changed too.)
Each exception object based on class xmsg is accompanied by a message
describing the reason for throwing the exception.
The next important notion is that an exception should have a private
copy (on the heap, presumably) of its message string. A typical exception
constructor allocates storage on the heap, copies the string, and sets an
internal flag to note that it has done so. That wa~ the destructor knows to
free the storage once the exception has been processed.
But then, why the flag? Well, one important exception derived from this
base class is xalloc (also renamed). It is thrown by operator new when it
fails to allocate storage. (See Chapter 4: <new>.) The last thing you want to
do is try to copy a string onto the heap when you have to report that there's
no more room on the heap! Thus, a special protected constructor lets you
avoid such copying of strings. Of course, anyone using this constructor had
better provide a string with a sufficiently long lifetime, or trouble ensues.
That's why this form is discouraged, except where absolutely necessaI)T.
raise You'd think then that to throw an exception you write something like:
throw xmsg (libad input record");
You can certainly do so, but that is not the preferred method. Instead, for
any exception object ex based on class xmsg, you're encouraged to call
ex. raise ( ). That member function does three things:
• First it calls (*ex. handler) (*this) to call the raise handler. The default
behavior is to do nothing, but you can hijack any thrown exception by
providing your own raise handler with a call to xmsg: : set_raise_han-
dler.
54 Chapter 3
• Then it calls the virtual member function do_raise. That lets you hijack
thrown exceptions only for some particular class derived from xmsg.
• Finally it evaluates the expression throw *this.
The first escape hatch is for embedded systems and those projects I
indicated above that abhor all exceptions. The program can reboot, or
longjmp to some recovery point (and to heck with the skipped destructors).
The second is for classes derived from xmsg that may require special
handling. You can intercept just these exceptions, without interfering with
any others. (But see page 64.)
The third thing is to do what exception classes were invented to do in
the first place. By having all library exceptions be thrown through this
machineI}T, however, the class meets the needs of several constituencies.
There's still more to library exceptions. A whole hierarchy of classes is
derived from xmsg:
exception xmsg
hierarchy xlogic
xdomain
badcast
badtypeid (typeinfo)
invalidargument (bits/bitstring/dynarray)
lengtherror (bits/bitstring/dynarray/string/wstring)
outofrange (bits/bitstring/dynarray/string/wstring)
xruntime
xrange
overflow (bits)
xalloc (new)
ios::failure (ios)
I list in parentheses after each exception class the other library classes that
use it. Some of these exception classes are defined in other headers, but
most are to be found in <exception>. There are two basic groups:
xlogic - logic errors, derived from class xlogic, which report errors that you can,
in principle, detect and avoid when writing the program
xrunt ime - runtime errors, derived from class xrunt ime, which report errors that you
can, most likely, detect only when you run the program
xdomain The former category is for those "can't happen" events that are often too
xrange hard to prevent, at least until after some thorough debugging. For example,
xdomain is derived from xlogic for reporting a domain error (invalid
argument value). The latter is for surprises that happen during program
execution, such as running out of heap or encountering bad input from a
file. xrange is derived from xruntime for reporting a range error (unrepre-
sentable result). No library functions throw objects of these classes directl)!,
however.
Exception processing code can also call two additional functions:
terminate - terminate, when exception handling must be abandoned for any of
several reasons, shown below
unexpected - unexpected, when a function throws an exception that is not listed in
its exception specification
<exception> 55
exception An exception specification is an optional qualifier such as throw (badcast,
specifications xalloc) that you append to a function declaration. It promises that the
function, when executed, will never propagate any exception not in the
specified list. The use and usefulness of exception specifications is still
being debated within the Committee.
A terminate condition occurs when the exception handling mechanism:
• cannot find a handler for a thrown exception
• finds the execution stack corrupted
• executes a destructor that tries to transfer control to a calling function by
throwing another exception
handlers The default behavior of terminate is to call the function abort, declared
in <stdlib.h>. The default behavior of unexpected is to call terminate.
As usual in C++, however, you can provide your own flavors of these
functions. A call to set_terminate lets you specify a pointer to a terminate
handler. The new function you specify must still somehow terminate the
program. A call to set_unexpected lets you specify a pointer to an unex-
pected handler. The new function you specify can itself throw (or rethrow)
an exception or terminate program execution.
As you can see, the facilities provided by <exception> give you consid-
erable latitude in reporting and handling exceptions. The C++ library uses
this machinery exclusivel)', so you can control what the library does with
exceptions. You can even prevent the library from actually evaluating any
throw expressions. Given our limited experience to date with using expres-
sions in C++, I'm fairly confident that this is basically a good design. It is
still being refined by the Committee, however.
set_ Establishes a new handler for terminating exception processing. The function
terminate stores new-p in a static object that, for the sake of exposition, can be declared
as:
fvoid_t* te~inste_nandler = &abort;
where the function signature abort () is defined in <stdlib.h>. new---p shall
not be a null pointer.
The function returns the previous contents of te.Dllinate_nandler.
17.3.2.12 set_unexpected (fvoid_t *)
fvoid_t* set_unexpected(fvoid_t* new-p);
set_ Establishes a new handler for an unexpected exception thrown by a function
unexpected with an exception-specification. The function stores new-p in a static object
that, for the sake of exposition, can be declared as:
fvoid_t* une~ected_bandler = &te~iDate;
new-p shall not be a null pointer.
The function returns the previous contents of unexpected_handler.
<exception> 63
17.3.2.13 terminate ( )
terminate void terminate();
Called by the implementation when exception handling must be abandoned for
any of several reasons, such as:
• when a thrown exception has no corresponding handler;
• when a thrown exception detennines that the the execution stack is corrupted;
• when a thrown exception calls a destructor that tries to transfer control to a
calling function by throwing another exception.
Using the notation of subclause 17.3.2.11, the function evaluates the expression:
(*te~inate_bandler)()
Footnotes:
Footnotes 76) An empty NTBS is also an empty NTMBS.
Future Directions
Practically every name has changed at least once in the exception hier-
archy since the Informal Review Draft quoted here. And the hierarchy itself
has been repeatedly rearranged. Here are the current names, and arrange-
ment. If the name has changed, I show the old name in parentheses:
revised exception (xmsg)
hierarchy logic_error (xlogic)
damai~error (xdomain)
invalid_argument (invalidargument)
length_error (lengtherror)
out_of_range (outofrange)
bad_cast (badcast)
bad_typeid (badtypeid)
runtime_error (xruntime)
range_error (xrange)
overflow_error (overflow)
bad_alloc (xalloc)
ios::failure
64 Chapter 3
<stdexcept> Moreover, some of the exceptions have been moved to a new header called
<stdexcept>. The Committee is still tinkering with naming conventions
to make the Standard C++ library more uniform.
what A more fundamental change is in the representation of message text.
Gone are the where and why components. The what component is now an
object of class string. (See Chapter 15: <string>.) In fact, the member
function exception: :what () is now virtual. A protected constructor lets
certain derived classes (runtime and a110c) avoid allocating storage for
the message. In this case, what () yields an implementation-defined result.
raise The Committee has eliminated the raise () machinery: Not everyone
appreciates the need for a standard way to control the handling of library-
generated exceptions. Others begrudge the amount of complexity involved
in solving the problem. The mechanism described here must thus be
reworked using secret names, or eliminated altogether.
It didn't help that the current machinery is underspecified. The whole
idea of an exception hierarchy is to provide nested levels of control.
Catching an exception of class xa110c should ignore all others, even if
they're derived from a common base. Catching an exception of class xlogic
should also catch any type derived from that class, but no exceptions
derived from xruntime. And catching an exception of class xmsg should
catch the whole family. But it takes a bit of work to pull all that off.
A class derived from xmsg must override the default definition for the
virtual member function do_raise (). For example:
void overf1ow::do_raise()
{ / / throw an overflow
throw *this;
}
Using <exception>
The header <exception> has two distinct uses:
• It defines much of the hierarchy of exception classes thrown by the
Standard C++ library: You include this header to use the defined classes
directl)', and not just in conjunction with other library classes.
• It declares the functions set_terminate and set_unexpected. You
include this header to register handlers that can be called when any
exceptions are later thrown.
exception I strongly encourage you to follow the lead of (this version of) the
classes Standard c++ library in the use of exceptions:
• Throw only objects of one or more classes derived from xmsg (or more
recently exception).
• Use the most appropriate exception class defined in the Standard C++
libra!)', wherever possible.
• If you must introduce a new exception class, derive it from the most
appropriate library exception class.
• If you introduce a new exception class, override do_raise to evaluate
the expression throw *this, as I showed above.
• Always throw an exception object ex by evaluating the expression ex.
raise ( ) . Even better, supply a function to throw each exception.
• Throw exceptions only to report conditions that require truly distinct
processing - where possible, favor "in-channel" status reporting, such
as returning special error values. (EOF and NPOS are two examples.)
Exceptions are not universally implemented. And the draft C++ Stand-
ard is particularly volatile in this area. So for the near to medium term, I
have to add another piece of advice. Encapsulate exception handling, to
localize and minimize change as implementations evolve.
_Nomemory I follow my own advice throughout this implementation. In the previous
chapter (page 48), I introduced the function _Nomemory ( ), declared in
<defines>. It reports an out-of-memory condition by throwing an object
of class xalloc (or more recently bad_alloc). Later in this book, you will
see secret member functions added to throw other exceptions peculiar to a
class. (See Chapter 15: <string>, Chapter 16: <wstring>, Chapter 17:
<bits>, Chapter 18: <bitstring>, and Chapter 19: <dynarray>.) I am also
careful to use only the what message component, since its two companions
have already been eliminated from the draft C++ Standard. I persist,
however, in calling the member function raise, for a variety of reasons.
66 Chapter 3
Encapsulation of exception handling has one additional advantage. You
may have to write code intended to run on a variety of implementations.
If some have implemented exceptions and others have not, the tendency is
strong to avoid using exceptions altogether. But that approach costs you
the use of a powerful language feature, one that often helps you better
structure a program. Better to start thinking now about how best to incor-
porate exception handling, even if you have to disable much of the machin-
eI)T, than to put off learning an important discipline.
set_ One transitional approach, using library exception classes as defined
raise_handler here, is to intercept all thrown exceptions before they actually evaluate a
throw expression. You can write something like:
#include <stdlib.h>
#include <iostre~
void ~_raise(xmsg& ex)
{ II abort OD any throw
cerr < "exception: n < ex.what() < endl;
abort();
}
xmsg::set_raise_handler(&~_raise);
If you follow this form, you can replace the call to unexpected ( ) with
code tailored to each function. Given the uncertain future of exception
specifications, you may be better off cultivating this style from the outset.
Implementing <exception>
exceptio Figure 3.1 shows the file exceptio, which implements the header <ex-
ception>. It consists mostly of declarations for the various exception
classes, beginning with the base class xmsg. Declarations for the various
exception handling functions bring up the rear.
copying and The draft C++ Standard doesn't declare any copy constructors or assign-
assignment ment operators whose overt behavior is what the translator supplies by
default. Similarly, I don't bother to provide any such creatures whose actual
behavior can be supplied by default. Here is a case, howeve:4 where actually
copying the member objects from one object of class xmsg to another is
inadequate. The result can be message strings that get freed more than once
- a debugging nightmare. So I declare xmsg as having an explicit copy
constructor and assignment operator, and provide definitions for them.
_Bool I have changed to type _Bool those declarations that have been changed
to type bool in the draft C++ Standard. The header <yxvals .h> defines
_Bool as type int, if the new keyword is not yet defined.
68 Chapter 3
Figure 3.1: II exception standard header
#ifndef _EXCEPTZON_
exceptio #define _EXCEPTZON_
Part 1 of 2 #include <defines>
II class xmsg
class xmsg (
public:
typedef void (*raise_handler) (xmsg&);
static raise_handler set_raise_handler(raise_handler);
xmsg(const char * = 0, const char * = 0, const char * = 0);
xmsg(const xmsg&);
xmsg& operatorc(const xmsg&);
virtual -xmsg();
void raise();
const char *what() const;
const char *where() const;
const char *why() const;
static void _Throw(xmsg *);
protected:
virtual void do_raise();
xmsg(const char *, const char *, const char *, _Bool);
private:
void _Tidy();
static raise_handler _Handler;
const char *_What, *_Where, *_Why;
_Bool _Alloced;
) ;
II class xlogic
class xlogic : public xmsg
public:
xlogic(const char * 0, const char * 0, const char * 0);
virtual -xlogic();
protected:
virtual void do_raise();
);
II class xruntime
class xruntime : public xmsg
public:
xruntime(const char * = 0, const char * 0,
const char *= 0);
virtual -xruntime();
protected:
xruntime(const char *, const char *, const char *, _Bool);
virtual void do_raise();
);
II class badcast
class badcast : public xlogic
public:
badcast(const char * = 0, const char * 0, const char * 0),
virtual -badcast();
protected:
virtual void do_raise();
) ;
II class invalidargwment
class invalidargument : public xlogic
<exception> 69
Continuing public:
invalidargument(const char * 0, const char * 0,
exceptio
const char * = 0);
Part 2 of 2 virtual -invalidargwment();
protected:
virtual void do_raise();
};
II class lengtherror
class lengtherror : public xlogic
public:
lengtherror(const char * = 0, const char * 0,
const char * = 0);
virtual -lengtherror();
protected:
virtual void do_raise();
};
II class outofrange
class outofrange : public xlogic
public:
outofrange(const char * = 0, const char * 0,
const char * = 0);
virtual -outofrange();
protected:
virtual void do_raise();
};
II class overflow
class overflow : public xruntime
public:
overflow(const char * = 0, const char * 0,
const char * = 0);
virtual -overflow();
protected:
virtual void do_raise();
};
II class xdomain
class xdomain : public xlogic
public:
xdomain(const char * = 0, const char * 0, const char * 0);
virtual -xdomain();
protected:
virtual void do_raise();
};
II class xrange
class xrange : public xruntime
public:
xrange(const char * = 0, const char * 0, const char * 0);
virtual -xrange();
protected:
virtual void do_raise();
};
II function declarations
fvoid_t *set_te~inate(fvoid_t *);
fvoid_t *set_unexpected(fvoid_t *);
void te~inate(), unexpected();
#endif o
70 Chapter 3
Figure 3.2: II exception -- exception members
#include <signal.h>
exceptio.c #include <stdlib.h>
Part 1of 2 #include <string.h>
#include <iostream>
xmsg::raise_handler xmsg::_Handler c 0;
return (*this);
xmsg: : -xmsg ( )
( / / destruct an xmsg
_Tidy ( );
}
<exception> 71
Continuing void xmsg::raise()
exceptio.c ( II raise an xmsg
if (_Handler)
Part 2 of 2 (*_Bandler) (*this);
do_raise();
_RAJ:SB(*this);
}
void xmsg::do_raise()
( II do no special raise handling by default
)
xlogic::-xlogic()
( II destruct an xlogic
}
void xlogic::do_raise()
{ II throw an xlogic
_RAISE(*this)i
} o
<exception> 73
Figure 3.4: II xruntime -- xruntime members
#inc1ude <exception>
xruntime.c
xruntime::xruntime(const char *what, const char *where,
const char *why)
xmsg(what, where, why)
{ II construct an xruntime
}
xruntime::-xruntime()
{ II destruct an xruntime
}
void xruntime::do_raise()
( II throw an xruntime
_RAISE(*this);
} o
xdomain::-xdomain()
{ II destruct an xdomain
}
void xdomain::do_raise()
( II throw an xdomain
_RAISE(*this);
} o
xdomain.c Two more classes are derived from these to report mathematical errors.
xrange. c Figure 3.5 shows the file xdomain. c, wllich implements the class xdomain.
Figure 3.6 shows the file xrange •c, which implements the class xrange.
badcast.c Figure 3.7 shows the file badcast.c, which implements the class bad-
cast. Objects of class badcast are thrown implicitly by expressions gener-
ated by the translator. The only other exception class on such in timate terms
with the language is badtypeid. (See Chapter 5: <typeid>.)
74 Chapter 3
Figure 3.6: II xrange -- xrange members
#include <exception>
xrange.c
xrange::xrange(const char *what, const char *where,
const char *why)
xruntime (what, where, why)
II construct an xrange
xrange::-xrange()
{ II destruct an xrange
}
void xrange::do_raise()
( I I throw an xrange
_RAJ:SE(*this);
} o
badcast::-badcast()
{ II destruct a badcast
}
void badcast::do_raise()
( II throw a badcast
_RAISE(*this);
} o
invalidargument::-invalidargument()
{ II destruct an invalidargument
}
void invalidargument::do_raise()
( I I throw an invalidargument
_RAISE(*this);
} 0
<exception> 75
Figure 3.9: II lengtherror -- lengtherror member functions
#include <exception>
lengther.c
lengtherror::lengtherror(const char *what, const char *where,
const char *why)
xlogic(what, where, why)
( II construct a lengtherror
)
lengtherror::-lengtherror()
( II destruct a lengtherror
)
void lengtherror::do_raise()
( II throw a lengtherror
_RAISE(*this);
) o
outofrange::-outofrange()
( II destruct an outofrange
)
void outofrange::do_raise()
( II throw an outofrange
_RAISE(*this);
) o
overflow::-overflow()
( II destruct an overflow
)
void overflow::do_raise()
( II throw an overflow
_RAISE(*this);
) o
76 Chapter 3
Figure 3. 12: IIte~inate -- te~inate support functions
#include <stdlib.h>
terminat.c #include <exception>
void te~inate()
{ II call te~inate handler or abort
if (te~inate_handler 1= 0)
(*te~inate_handler)();
abort();
} 0
void unexpected()
{ II call unexpected handler or te~inate
if (unexpected_handler 1= 0)
(*unexpected_handler){);
te~inate{);
} 0
invalida.c The remaining four files in this group are much the same. They imple-
lengther.c ment the exception classes thrown by other classes in the Standard C++
outofran.c library: Figure 3.8 shows the file invalida. c, which implements the class
overflow.c invalidargument. Figure 3.9 shows the file lengther.c, which imple-
ments the class lengtherror. Figure 3.10 shows the file outofran. c, which
implements the class outofrange. And Figure 3.11 shows the file over-
flow. c, which implements the class overflow.
terminat. c Finall)', Figure 3.12 shows the file terminat. c, which defines the func-
unexpect.c tions terminate and set_terminate. And Figure 3.13 shows the file
unexpect. c, which defines the functions unexpected and set_unex-
pected. Both are straightforward.
<exception> 77
Testing <exception>
texcepti. c Figure 3.14 shows the file texcepti. c. It faces the difficult task of testing
as much of <exception> as possible even when exceptions are not imple-
mented. And because it deals with various extraordinary transfers of
control, the logic is rather twisty: Ironicall)T, the tests depend heavily on
setjmp and longjmp - two functions designed to be obsoleted by excep-
tion handling.
The program tests four ways that control can be transferred:
• to a do_raise () member function of a derived exception class
• to a raise handler registered with set_raise_handler
• to a terminate handler registered with set_terminate
• to an unexpected handler registered with set_unexpected
All four tests report a failure to transfer control properly with the idiom:
assert("message ll == 0);
Since a string literal never converts to a null pointer, the assertion always
fails. That prints the message, along with source file and line number
identification, then exits abnormally:
As a grand finale, the program attempts to throw an exception and catch
it. To do so, it uses the _RAJ:SE macro I described on page 72 to throw the
exception, if possible. It also uses yet another set of related macros, defined
in <yxvals.h>, designed to catch an arbitrary exception:
_TRY_BEGl:N _TRY_BEGl:N
_CATCH_ALL II try block
_CATCH_END II catch block
_CATCH_END
If exceptions are implemented, these macros expand to:
try {
II try block
} catch ( ••. )
II catch block
}
II static data
static jmp_buf jbuf;
II derived exception
class myex : public xmsg {
public:
myex(const char *what 0)
: xmsg(what) {}
protected:
virtual void do_raise();
};
void myex::do_raise()
{ II capture raised exception
longjmp(jbuf, 1);
}
void abort_hand(int)
{ II field abort when throwing exception
cout « tlExceptions are not implemented tl « endl;
cout « tlSUCCESS testing <exception>" « endl;
exit(O);
}
void jmpback ( )
{ II longjmp back to caller
longjmp(jbuf, 1);
}
void raiseback(xmsg&)
{ II jump back from raise handler
longjmp(jbuf, 1);
}
void try_myex_hand()
{ II test do_raise machinery
if (setjmp(jbuf) == 0)
{ II see if handler returns control
myex(lItesting myex::do_raisell).raise(};
as sert ( IImyex: : rai se () returned" == 0);
}
<exception> 79
Figure 3. 15: void try_raise_hand ()
( II test set_raise_handler machinery
texcepti.c xmsg::raise_handler save_hand =
Part 2 of 2 xmsg::set_raise_handler(&raiseback);
if (setjmp(jbuf) ==
0)
( II see if handler returns control
xmsg("testing raise handler").raise();
assert("xmsg::raise() returned" ==
0);
}
assert(xmsg::set_raise_handler(save_hand) &raiseback);
}
void try_terminate()
( II test te~inate machinery
fvoid_t *save_hand = set_te~inate(&jmpback);
if (setjmp(jbuf) ==
0)
te~inate(), assert("te~inate() returned" 0);
assert(set_te~inate(save_hand) ==
&jmpback);
}
void try_unexpected ( )
( II test unexpected machinery
fvoid_t *save_hand set_unexpected(&jmpback);
if (setjmp(jbuf) ==
0)
unexpected(), assert ("terminate() returned" 0);
assert (set_unexpected(save_hand) == &jmpback);
}
int main ( )
( II test basic workings of exception definitions
xmsg xl("xmsg what", "where", "why");
xlogic x2("xlogic what", "where", "why");
xruntime x3("xruntime what", "where ll , IIwhyll);
badcast x4("badcast what", "where", "whyn);
invalidargument x5("invalidargument what", "where", "why");
lengtherror x6("lengtherror what-, "where", "why");
outofrange x7("outofrange what", "where", "why");
overflow x8("overflow what", "where", "why");
xdomain x9("xdomain what", "where", "whyn);
xrange xl0("xrange what .. , "where'·, IIwhy");
try_JDYex_hand();
try_raise_hand();
try_terminate();
try_unexpected();
cout « "About to throw an exception" « end1;
signal(SZGABRT, &abort_hand);
_TRY_BBGJ:N
_RAJ:SB(xl), assert("throw returned" == 0);
_CATCH_ALL
cout « "Bxceptions are implemented" « endl;
_CATCH_END
cout « "SUCCBSS testing <exception>" « endl;
return (0);
o
80 Chapter 3
Exercises
Exercise 3. 1 An argument value on a function call is determined by a nontrivial expres-
sion evaluation. If the value is invalid for the function specification, does
that constitute a logic error or a runtime error?
Exercise 3.2 If a constructor throws an exception, should its corresponding destructor
be called?
Exercise 3.3 If a constructor allocates storage and it can possibly throw an exception
(other than for a storage-allocation failure), how can you avoid "memory
leaks," where storage is repeatedly allocated and never gets freed?
Exercise 3.4 You need to construct an object that can throw two or more different
exceptions during construction. Describe a discipline that avoids memory
leaks in all cases.
Exercise 3.5 Rewrite the macro assert, defined in <assert.h>, to throw an exception
when the assertion fails. The message stored with the exception object
should convey at least as much information as the existing assert macro
displays (source file name, source line number, and the source code of the
offending predicate).
Exercise 3.6 [Harder] Can you implement setjmp and longjmp purely in terms of
exceptions? If so, do so. If not, explain why not.
Exercise 3.7 [Harder] Can you implement exceptions purely in terms of setjmp and
longjmp? If so, do so. If not, explain why not.
Exercise 3.8 [Very hard] Write a version of setjmp and longjmp that calls all necessary
destructors, and properly restores all objects with dynamic storage dura-
tion, on a longjmp call. Why would you want such a capability?
Chapter 4: <new>
Background
Exceptions can be thought of as a way to structure the use of setjmp and
longjmp, as I discussed in the previous chapter. Similarly, the addition of
new and delete to C++ essentially structures the use of malloe and free.
By writing the declaration:
Thing *p = new Thing;
you are assured that the program constructs the object of type Thing after
it is successfully allocated and before it can be accessed throughp. Similarly,
the expression statement:
delete p;
ensures that the program destroys the object before it deallocates its storage.
You don't have to include any headers before writing expressions like
these - new and delete are indeed built right into the language. But you
can also playa variety of games with storage allocation if you choose. To
do so, you begin by including the header <new>. Then you can supply a
handler function that gets control when an allocation request can't be
honored. Or you can even replace one or more of the Standard C++ library
functions that manage storage with a function definition in the program.
treaties As an aside, the draft C++ Standard is a bit harder to write than the C
Standard. In C, the implementation provides all Standard C library func-
tions and you the programmer cannot replace them. The C Standard has to
describe only a single interface between implementation and program. In
C++, however, the program can replace functions otherwise supplied by
the Standard C++ library: The draft C++ Standard must spell out the
environment promised to such a displacing function. And it must spell out
what is expected of the displacing function so the program doesn't get
surprised.
Here is another example from the previous chapter. A terminate handler
(called by the function terminate (» is not supposed to return to its caller.
If you provide one that prints a message and returns, you can cause the
Standard C++ library severe problems. The draft C++ Standard says so.
That gives the library permission to misbehave if you don't hold up your
end of the bargain. So when you read the descriptions that follow in this
chapter, remember that the "treaty" between programmer and implemen-
82 Chapter 4
tor can be multifaceted. The extra complexity of the draft C++ Standard is
one of the prices you pay for extra flexibility in this area.
set The simplest game you can play with the storage-allocation functions is
new_handler to gain control when an allocation request can't be honored. The function
set_new_handler lets you register a new handler for this condition. In
principle, the draft C++ standard says you can "make more storage avail-
able for allocation and then return," but the method you use will be
implementation dependent. Calling free to liberate storage may help, but
there is no requirement that storage be actually allocated by callingmalloe.
Deleting one or more allocated objects may also help, but even that is not
guaranteed. More likeI)!, you will want to throw an exception or terminate
execution at this point.
exceptions The default new handler does, in fact, throw an exception, of class xalloe
(or, more recently, bad_alloe). As I described in the previous chapter, all
library exceptions derive from the base class xmsg (or, more recentl)T,
exception). Special provision is made for constructing an xalloe object
without allocating storage on the heap. Moreover, the Standard C++ library
(currently) throws all exceptions by calling ex. raise (), for some object ex
of class xmsg. Unless you seize control of the process in one of the ways I
described earlier, the eventual outcome is that a failed allocation will throw
an exception, which will in turn terminate execution of the program.
null This is a significant cllange from universal past practice, which has been
pointers to quietly yield a null pointer as a result of the new expression. The
Committee anguished quite a bit before making this change. But eventually,
the predominant wisdom was that the Standard C++ library had bloody
well better use the full language in this case, not just the bits that were
available when new and delete were first added to C++.
A persuasive argument is that very few programs truly check all new
expressions for null pointers. Those that don't may well stumble about
when the heap is exhausted - they're almost certainly better off dying a
clean death. Those that do check all such expressions often simply abort -
the path to abnormal termination is now just slightly different. It is only
those few sophisticated programs that try to do something nontrivial when
the heap is exhausted that need a bit of rewriting. Most of the Committee
felt this was a necessary price to pay to introduce exceptions at this critical
juncture.
Even so, some sympathy remains for being able to revert to the old
behavior. For a variety of reasons, the Committee has not spelled out a
portable way to do so. But it has identified what it thinks should be a
common extension. Calling set_new_handler with a null pointer argu-
ment is otherwise undefined behavior. It seems natural to use this nonport-
able call as a way for an implementation to know that it should revert to
the older behavior.
operator If you want more certain control over the business of allocating storage,
new your best bet is to provide your own versions of operator new (size_t)
<new> 83
and/ or operator delete (void *). These functions have a peculiar dis-
pensation - the Standard C++ library provides a version of each, but you
can replace (or "knock out") those versions by defining your own. (Only
the array versions of these two operators, described belo~ also enjoy this
special status within the Standard C++ libral)T.)
But first note an important distinction. When you write:
Thing *p = new Thing;
the subexpression new Thing is called a new expression. It calls operator
new(size_t) to allocate storage, but it also does other things, such as
constructing the newly allocated object. All that operator new(size_t)
has to worry about is providing the number of requested "raw" bytes,
suitably aligned, or dealing with inadequate heap storage. Similarly:
delete p;
operator is an expression statement containing a delete expression. It calls operator
delete delete (void *) to free storage, but it first calls the destructor for the object
(only if the pointer is not null, of course). All that operator delete (void
has to worry about is freeing storage for the object.
*)
Footnotes:
Footnotes 77) Note that where_arg and wny_srg must either be null pointers or point
to NTMBSs whose lifetime exceeds that of the constructed object.
78) The value must not have been invalidated by an intervening call to opera-
tor delete (size_t), or it would be an invalid argument for a Standard
c++ library function call.
79) The value must not have been invalidated by an intervening call to opera-
tor delete[] (size_t), or it would be an invalid argument for a
Standard C++ library function caU.
80) The value cannot legitimately compare equal to one that has been invali-
dated by a call to operator delete (size_t), since any such compari-
son is an invalid operation.
81) A common extension when new_handler is a null pointer is for operator
new(size_t) to return a null pointer, in accordance with many earlier
implementations of C++.
82) It is not the direct responsibility of operator new [] (size_t) or op-
erator delete [] (void*) to note the repetition count or element size
of the array. Those operations are performed elsewhere in the array new and
delete expressions. The array new expression, may, however, increase the
size argument to operator newt] (size_t) to obtain space to store
supplemental information.
Future Directions
As I mentioned in the previous chapter, the exception class xalloc has
been renamed bad_alloe, with rather different properties inherited from
its base class. Its definition has twice been moved, but is currently back in
the header <exception>.
Otherwise, this header has been reasonably stable of late.
Using <new>
You include <new> only if you want to take extraordinary control of the
storage-allocation process. Left to its own devices, the Standard C++ library
allocates all storage off a common heap. If it can't honor an allocation
request, it throws an exception. And if exception handling is left to its own
devices, the thrown exception causes the program to terminate abnormally.
For many programs, that's acceptable behavior.
You may do some storage allocation of your own. If so, a polite way for
your specialized allocator to return failure is to mimic what the Standard
c++ library does. In principle, that means you should include <new>,
<new> 89
declare an object ex of class xalloc, and call ex.raise(). In practice, I
encourage you not to be so direct. The exception machinery of the Standard
C++ library is still volatile. Better you should call some common function
to do this job for you.
This implementation includes the function _Nomemory, declared in <de-
fines>. (See page 48.) It encapsulates the business of reporting a storage-
allocation failure. If you can't or won't use this function, I encourage you
to write a similar one and use it religiously.
The traditional method for gaining control when storage allocation fails
is to register a new handler by calling set_new_handler. But as I discussed
earlier (on page 82), this facility offers only limited control. You may have
code that does something useful with an existing implementation of C++,
but don't expect it to be portable to another.
A more likely reason to call set_new_handler might be to revert to
another traditional behavior. You may have code that expects a new expres-
sion to yield a null pointer when storage allocation fails. The call:
set_new_handler(O);
while not defined by the draft C++ Standard, is suggested as the way to roll
back the clock. For any new code you write, however, I strongly urge you
not to consider such antics.
replacing You may have occasion to knock out some or all of the storage-allocation
functions functions. If you do, keep such functions simple, lest they call a function
that allocates storage. And note that only certain combinations make sense:
• You can clloose to replace only operator delete (void *) and/ or
operator delete [] (void *), provided you make no attempt to actu-
ally free the storage.
• If you replace operator new ( si ze_T) , you must also replace operator
delete (void *).
• If you replace operator new [ ] (size_T), you must also replace opera-
tor delete[] (void *).
I suspect, however, that you have little occasion to replace the array
versions.
Implementing <new>
new Figure 4.1 shows the file new. It (currently) declares the exception class
xalloc. Otherwise, its principal business is to declare the various functions
associated with storage allocation.
_HAS_ The array versions of these functions are a fairly recent invention. An
ARRAY_NEW implementation that doesn't recognize the new forms must be protected,
lest it diagnose syntax errors in the header. Thus, the header <yxvals .h>
supplies yet another implementation-dependent indicator. It defines the
macro _HAS_ARRAY_NEW only if the implementation recognizes the new
array versions.
90 Chapter 4
Figure 4.1: II new standard header
#ifndef _NEW_
new
#define _NEW_
#include <exception>
II class xalloc
class xalloc : public xruntime
public:
xalloc(const char * = 0, const char * 0,
const char * =
0);
virtual -xalloc();
protected:
virtual void do_raise();
} ;
II function and object declarations
fvoid_t *set_new_handler(fvoid_t *);
void operator delete(void *);
void *operator new(size_t);
inline void *operator new(size_t, void *_P)
{return (_P); }
#if _HAS_ARRAY_NEW
void operator delete[] (void *);
void *operator new[] (size_t);
inline void *operator new[] (size_t, void *_P)
{return (_P); }
#endif
extern fvoid_t (*_New_hand);
#endif o
new.c Figure 4.2 shows the file new. c. It supplies all the machinery you need
to call or register new handlers. Note that this includes the various support
functions for the exception class xalloc. Here is also where the implemen-
tation-specific function _Nomemory resides.
newop.c Four functions must each reside in separate modules, so they can be
delop.c replaced. Figure 4.3 shows the file newop. c, which implements the function
operator new ( size_t). This implementation calls malloc, declared in
<stdlib.h> to do the actual allocation. It loops until storage allocation
succeeds or the new handler fails to return. Figure 4.4 shows the file
delop.c, which implements the companion function operator de-
lete (void *). It naturally enough calls free, also declared in <stdlib. h>,
the companion to malloc.
newaop.c Figure 4.5 shows the file newaop • c. It implements the function operator
delaop.c new[] (size_t), which lets operator new(size_t) do all the work. Note
the use of macro _HAS_ARRAY_NEW to guard the new function syntax. For
an older implementation, this source file yields a translation unit with no
definitions. Figure 4.6 shows the file delaop.c. As you might expect, it
relies on operator delete (void *) and it too is guarded by the same
macro.
<new> 91
xalloc::-xalloc{)
{ II destruct an xalloc
}
void xalloc::do_raise{)
{ II throw an xalloc
_RAISE{*this);
}
void _Nomemory ( )
{ II report out of memory
nomem.raise{);
} o
Testing <new>
tnew.c Figure 4.7 shows the file tnew.c. It tests the basic functionality of the
storage-allocation functions supplied by the Standard C++ library: The only
function it attempts to knock out is operator delete [] (void *). Natu-
rally enough, it makes that test only if _HAS_ARRAY_NEW says it's wise to
even try. In this regard, tnew. c is not a portable test for an arbitrary
implementation of the Standard C++ libraI')T. To do a proper job of that
would require a suite of additional programs, which I chose not to provide
in this book.
One useful service comes with this compromise in portability The
program reports whether or not an implementation supports the array
operators. If all goes well, the program prints either:
Array new/delete are replaceable
SUCCESS testing <new>
or
Array new/delete are not replaceable
SUCCESS testing <new>
and takes a normal exit.
<new> 93
Figure 4.7: II test <new>
#include <assert.h>
tnew.c #include <string.h>
#include <new>
#include <iostream>
II class abc
class abc {
public:
abc(const char *s)
{strcpy(buf, s);
char *ptr()
{return (buf);
private:
char buf[10];
};
II static data
static int del_called = 0;
int main ( )
{ II test basic workings of new definitions
xalloc x1("xalloc what", "xalloc where", "xalloc why");
II test set_new_handler
fvoid_t *save_hand = set_new_handler(&:nop);
assert (set_new_handler(save_hand) == &:nop);
II test new, placement new, and delete
abc *p = newabc("first");
assert(strcmp(p->ptr(), "first") == 0);
assert(new (p) abc ("second") == (void *)p);
assert(strcmp(p->ptr(), "second") == 0);
delete p;
II test array new, array delete
char *s = new char[10];
delete[] s;
cout « "Array new/delete are ll
« (del_called? II II : II not ")
« "replaceable" « endl;
cout « "SUCCESS testing <new>" « endl;
return (0);
o
94 Chapter 4
Exercises
Exercise 4. 1 Can you use a placement new expression to "reconstruct" an arbitrary
object? If not, can you list the constraints that must be satisfied for this
operation to be safe?
Exercise 4.2 Where does the program store size information when a new expression
allocates an array object? If you can't determine how a given implementa-
tion does the job, suggest one or more mechanisms that might work.
Exercise 4.3 Some people have suggested the need for a "renew" operator, which
reallocates storage for an object that changes size. (It is inspired in part by
the function realloc, declared in <stdlib.h>.) Specify a syntax for such
an operator, and sensible semantics to go with it. What do you do about
calling constructors and destructors?
Exercise 4.4 Write replacement versions of operator new(size_t) and operator
delete (void *) that report "storage leaks" (a failure to free all allocated
storage between two matching checkpoints. Why would you want such a
capability?
Exercise 4.5 Many programs allocate and free numerous objects of just a few different
sizes. Write replacement versions of operator new ( size_t) and opera-
tor delete (void *) that perfonn faster (on average) under these circum-
stances. Measure performance for a nontrivial program, with and without
your replacement functions, to demonstrate any improvement.
Exercise 4.6 Write replacement versions of operator new(size_t) and operator
delete (void *) that detect most or all invalid attempts to free storage.
Exercise 4.7 [Harder] Alter the specification for new handlers, operator new (size_t) ,
and operator delete (void *) so that a new handler can portably supply
additional storage. Why would you want such a capability?
Exercise 4.8 [Very hard] Write portable versions of operator new ( si ze_t) and opera-
tor delete (void *) that do not allocate storage by calling malloe. Do
you have to add any features to the language or to the Standard C++ library
to support this code?
Chapter 5: <typeinfo>
Background
A relatively recent significant addition to the draft C++ Standard is
"runtime type identification" (or RTTI, for sllort). Basically, it adds the
operator typeid for obtaining various bits of information on the type of an
object (or expression). The expression typeid x yields an object of class
typeinfo, defined in the header <typeinfo>.
RTTI was added to Standard C++ to llelp solve a few nagging problems.
Fundamental to the design of C++ is the notion of static typing. The
translator knows enough about the type of every operand and subexpres-
sion to make safe, and reasonably efficient, decisions about how to translate
each subexpression into executable code. It can do so even in the presence
of derived classes with virtual member functions. You can pretend that an
object of a derived class is an object of the base class, so long as you confine
yourself to operations valid for the base class alone. You can even tailor
behavior defined for the base class a bit by overriding virtual member
functions in the derived class.
But you can't do everything. Sometimes, you have a pointer to an object
of some base class and you really want to know the actual derived type of
the object. With multiple inheritance (more than one base class) and deri-
vation to arbitrary depth, this can be a nontrivial question to answer. A
traditional way to determine an actual derived type is to provide a "tag"
member object in the base class. Each time you construct a derived object,
you make a point of initializing the tag to describe the actual derived type.
Such a practice is an error-prone duplication of the work of the type system,
however. Alternatively, you can define a virtual member function that
reports the type of the object. You provide an overriding definition in each
derived class. Tllis beats having to get all those constructors right, but it is
still repetitious.
dynamic Still another method has been recently introduced into the Standard C++
casts language. A dynamic-cast expression can be used to safely "down cast" a
pointer to an object of a base class so that it points at the containing object
of some derived class. The result is a null pointer if the pointer does not
truly designate an object of the proper derived type. This solves many
type-identification problems, but it still leads to some tedious code.
96 ChapterS
Enter RTf!. You can apply the operator typeid to an arbitrary expres-
sion. For many expressions, the type can be determined statically at trans-
lation time. For a pointer to an object of some base-class type, however, the
program may have to do some work at runtime. In either event, the result
is an object of class typeinfo.
badtypeid The exception badtypeid is reported in some cases where the type
cannot be determined statically at translation time. If, in the process of
chasing down the actual object, the program encounters a null pointer, you
can guess what happens.
name What can you do with an object of class typeinfo? Well, you can obtain
some sort of name for the type, for one thing. typeinfo: : name () yields a
null-terminated multibyte string (NTMBS) that presumably says some-
thing meaningful about the type. There are no standard names defined, so
far, not even for the builtin types.
operator== You can also compare two objects of class typeinfo for equality or.
operator! = inequalit)T. Within any given program, you can expect two such objects to
compare equal only if they derive from two expressions of the same type.
Don't expect to be able to remember these critters in files, however, and
check for type equality across programs. Even running the same program
twice doesn't promise to yield the same representation of a typeinfo object
for the same type each time.
before Finally, you can impose an ordering on all the types within a program.
typeinfo: :before (const typeinfo&) returns nonzero for an object that
represents a type earlier in the pecking order than the argument object.
Once again, however, no promises are made about the rules for determin-
ing this order, or whether they're even the same each time you run the
program.
Far more can be said about the uses of RTfI, but this is not the place to
say it. I know of little practical experience with the standardized form of
this new feature. For now, I can report only on what facilities the Standard
C++ library provides in support of RTTI.
The class badtypeid defines the type of objects thrown as exceptions by the
implementation to report a null pointer p in an expression of the fonn typeid
(*p).
17.3.4.1.1 badtypeid: :badtypeid()
constructor badtypeid();
Constructs an object of class badtypeid, initializing the base class xlogic
with an unspecified constructor.
17.3.4.1.2 badtypeid: :-badtypeid()
destructor virtual Nbadtypeid();
Destroys an object of class badtypeid.
17.3.4.1.3 badtypeid: : do_raise ( )
do_raise II virtual void do_raise(); inherited
Behaves the same as xmsg: : do_raise ( ) .
17.3.4.2 Class typeinfo
Class class type info {
typeinfo public:
virtual -typeinfo();
int operator==(const typeinfo& rns) const;
int operatorl=(const typeinfo& rns) const;
int before(const typeinfo& rns);
const char* name() const;
private:
I I const char* name; exposition only
II T dese; e~osition only
typeinfo(const typeinfo& rns);
typeinfo& operator=(const typeinfo& rns);
};
The class typeinfo describes type infonnation generated within the program
by the implementation. Objects of this class effectively store a pointer to a name
for the type, and an encoded value suitable for comparing two types for equality
or collating order. The names, encoding rule, and collating sequence for types
are all unspecified and may differ between programs.
For the sake of exposition, the stored objects are presented here as:
• const char* name, points at a static NTMBS;
• T desc, an object of a type T that has distinct values for all the distinct types
in the program, stores the value corresponding to name.
17.3.4.2.1 typeinfo: : -typeinfo()
destructor virtual Ntypeinfo();
Destroys an object of type typeinfo.
17.3.4.2.2 typeinfo: :operator== (const typeinfo&:)
operator== int operator==(const typeinfo& rns) const;
Compares the value stored in desc with rhs. desc. Returns a nonzero value if
the two values represent the same type.
98 Chapter 5
17.3.4.2.3 type info : : operator 1= (const typeinfo&)
operator! = int operator!=(const typeinfo& rbs) const;
Returns a nonzero value if ! (*this == rhs).
17.3.4.2.4 typeinfo: : before (const typeinfo&)
before int hefore(const typeinfo& rbs) const;
Compares the value stored in desc with rhs. desc. Returns a nonzero value if
*this precedes rhs in the collation order.
17.3.4.2.5 typeinfo : : name ( )
name const char* name() const;
Returns name.
17.3.4.2.6 typeinfo: : typeinfo (const typeinfo&:)
constructor typeinfo(const typeinfo& rbs);
Constructs an object of class typeinfo and initializes name to rhs. name and
descto rhs.desc. 83 )
17.3.4.2.7 typeinfo: : operator=(const typeinfo&)
operator= typeinfo& operator=(const typeinfo& rbs);
Assigns rhs. name to name and rhs. deBc to desc. The function returns
*this.
Future Directions
The exception class has been renamed. The class badtypeid is currently
bad_typeid, derived from the class logic. This is part of a general renam-
ing of exceptions. (See Chapter 3: <exception>.)
constructor Class typeinfo currently declares the copy constructor and assignment
operator private to the class. Both should probably be protected instead,
but this issue has yet to be addressed by the Committee.
standard There is ongoing debate about whether there should be standard names
names for the scalar types (the predefined types mostly inherited from Standard
C). Some even argue that there should be rules for determining the spelling
of an arbitrary type name. Type names could then be preserved and
interchanged with different program executions.
name But this requirement skirts dangerously close to the business of "name
mangling mangling," where type information is appended to external names to
implement type checking through traditional linker technology. The Com-
mittee has resisted the temptation to standardize how names get mangled,
in part because such tricks are not an essential part of all Standard C++
implementations. A weaker requirement may be that the names of all scalar
types and types defined in the Standard C++ library are standardized.
My bet is that modifications to this subclause will appear only after the
user community gains more experience with this particular method of
specifying RTTI.
<typeinfo> 99
Implementing <typeinfo>
typeinfo Figure 5.1 shows the file typeinfo. It declares the exception class
badtypeid and the RTTI class typeinfo. Both are fairly simple.
_Typedesc Once again, I invoke the implementation-specific header <yxvals.h>.
In this case, it provides a definition of the type _Typedesc, the type chosen
by the implementation to encode type identification. Absent any guidance
from an implementation that supports RlTI, I have currently chosen to
define this as type int.
100 Chapter 5
Figure 5.1: II typeinfo standard header
#ifndef _TYPEINFO_
typeinfo #define _TYPEINFO_
#include <exception>
II class badtypeid
class badtypeid : public xlogic
public:
badtypeid(const char * = 0, const char * 0,
const char * = 0);
virtual -badtypeid();
protected:
virtual void do_raise();
};
II class typeinfo
class typeinfo (
public:
virtual -typeinfo();
_Bool operator==(const typeinfo&) const;
_Bool operatorl=(const typeinfo& _Rop) const
{return (I (*this == _Rop»; }
_Bool before(const typeinfo&) const;
const char *name() const
{return (_Name); }
private:
const char *_Name;
_Typedesc _Desc;
typeinfo(const typeinfo&);
typeinfo& operator=(const typeinfo&);
};
#endif o
typeinfo. c Figure 5.2 shows the file typeinfo. c. It defines all the support functions
needed to manipulate objects of class typeinfo. Note the assumptions
made here about objects of type _T'YPedesc:
• You can assign such objects with the conventional assignment operator.
• You can compare such objects for equality with operator==.
• You can determine whether an object precedes another with operator<.
A non-scalar implementation of _Typedesc must supply these definitions.
badtypei. c Finally, Figure 5.3 shows the file badtypei. c. It defines the usual mem-
ber functions for an exception class.
Testing <typeinfo>
ttypeinf. c Figure 5.4 shows the file ttypeinf .c. It tests just the most basic proper-
ties of the library support for RTTI. For one thing, there is little to test so
far. For another, I have access to no system that actually implements RlTI.
_HAS_ As is my practice, I provide yet another parameter to guard implemen-
TYPEINFO tations that lack a recent language addition. The header <yxvals .h> de-
fines the macro _HAS_TYPEINFO only for implementations that support the
<typeinfo> 101
Figure 5.2: II type info -- typeinfo members
#include <typeinfo>
typeinfo.c
typeinfo::-typeinfo()
{ II destruct a typeinfo
}
badtypeid::-badtypeid()
{ II destruct a badtypeid
}
void badtypeid::do_raise()
( II throw a badtypeid
_RA:ISE(*this);
} o
Exercises
Exercise 5. 1 A catch clause catches only thrown objects of a suitable type. Can you use
exception handling to determine the actual derived type of an object?
Exercise 5.2 What should the standard RTII names be for the scalar types?
Exercise 5.3 [Harder] Devise a rule based on these names that predicts the name of an
arbitrary type.
Exercise 5.4 [Very hard] Add the constructor typeinfo (const char *tnam.e) to class
typeinfo. For a valid RTII name (from the previous exercise) it should
construct an object tinfo that yields the same name on the call
tinfo. name () and compares equal to other such objects with the same
name. Write this constructor. Why would you want such a capability?
int main()
{ II test basic workings of typeinfo definitions
badtypeid xl ("badtypeid what", "where", "why" ) ;
typeinfo *pl 0;
typeinfo *p2 = 0;
#if _HAS_TYPEINFO
pl typeid (xl);
p2 = typeid (int);
#endif
cout « "RTTI is" « =
(pl 1 0 ? n not ")
« "implemented" « endl;
if (pl 1= 0)
( II test typeinfo member functions
assert(p2 1= 0);
assert(I(*pl == *p2»;
assert(*pl 1= *p2);
assert (pl->before(*p2) I I p2->before(*pl»;
assert(strcmp(pl->name(), p2->name(» 1= 0);
)
cout « "SUCCESS testing <typeinfo>" « endl;
return (0);
o
•
Chapter 6: <10S>
Background
The largest single component of the Standard C++ library is the package
called "iostreams." It consists of a whole slew of classes that work together
to make input and output look simple. The commonest application is
probably the classic:
#include <iostream>
cout « IIHello, world. 1I « endl;
which writes the message Hello, world. followed by a newline character
to the standard output stream (the same stream as controlled by stdout).
This simple expression is the tip of a rather large iceberg. Look a bit
below the surface. As you might guess, the header <iostream> declares all
the necessary classes and objects. The class of interest for this example is
ostream, each of whose objects controls a stream into which you can insert
characters. That stream can be an output file, a text string that grows
dynamically in memol)T, or lots of other things.
cout In this particular case, cout is a static object of class ostream whose name
has external linkage. It conspires rather intimately with stdout, the famil-
iar Standard C library object of type pointer to FILE, to help you write
formatted text to the standard output stream. You can freely intermix
expressions such as the one above with more traditional calls such as to
putchar or printf and get the result you'd expect. (Translation: you don't
have to worry about two different buffering mechanisms saving up char-
acters and writing reordered blocks of characters to the standard output
stream. Again, existing implementations don't always make this promise,
at least not unless you perform some magic incantation at runtime. And
then you can often expect a degradation of performance. The draft C++
Standard hopes to encourage better synchronization of C and C++ I/O, if
only to the standard streams.)
The header <iostraam> declares two similar objects of class ostream:
carr. carr, which controls writes of unbuffered text to the standard error
stream, in cooperation with stderr, and
clog. clog, which also cooperates with stderr but which won't necessarily
force text to be written out at the end of each insertion operation.
104 Chapter 6
Now take a closer look at the first part of the example expression:
cout « "Hello, world." •.•••
inserters The class ostream overloads the left-shift operator repeatedly with mem-
ber functions to provide a rich set of inserters. These each encode a right-
hand operand by various rules (akin to the conversion specifications for
printf), then insert the encoded text in the stream controlled by the
ostream object. In this particular case, the inserter called is:
ostream& ostream::operator«(const char* s);
This function knows to write the null-terminated string pointed to by s
to the output stream controlled by couto Put another wa~ the function
inserts each of the characters from the string into the controlled stream up
to but not including the terminating null.
More complicated inserters tum binary integer and floating-point val-
ues into sequences of characters that human beings can read. For example,
the inserter:
ostream& ostream::operator«(int n);
lets you write expressions like:
cout « i;
that inserts, say, the sequence -123 in the controlled stream to represent the
value -123 stored in the integer object i.
C programmers have been doing the same sort of thing for decades by
writing:
printf ("%s 9'od\n", s, i);
So what's the big deal? Well, the inserter approach offers several advan-
tages:
• The translator picks the appropriate inserter to match the right-hand
operand type. No need to make sure that conversion specifications in a
format string line up with the proper arguments. There is that much less
chance that the value will be interpreted incorrectl~
• The notation is often convenient. You can string inserters out left to right,
as in the original example above, to perform a series of insertions one
after the other.
• The notation can be augmented in various clever ways, as with the endl
in the original example.
manipulators I won't elaborate much on that last point until later. (See pages 124 and
244.) All you need to know for now is that inserting endl in an output
stream has the effect of inserting a newline character ( , \n 1) then flushing
output to the controlled stream. It is but one of many interesting manipula-
tors you can use with the inserter notation.
Other manipulators make up for one of the shortcomings of the inserter
notation. Remember that with printf you can write some pretty fancy
conversion specifications, such as:
printf("%+10d\n", i);
<ios> 105
to force a plus sign on positive output and pad the generated text to (at
least) ten characters. But operator« takes only two operands. There is no
place to smuggle in that extra formatting information.
The solution is to squirrel away the extra information in the cout object
before performing the insertion. You can do this by calling member func-
tions, as in the sequence of expression statements:
cout.setf(showpos), cout.width(10);
cout < i;
Or you can use still more magic manipulators to achieve the same effect, as
in the single expression statement:
cout « showpos « setw(10) « i;
As you can see, it's possible to have manipulators that take arguments. But
that involves even more chicanery - a topic for much later. (See Chapter
10: <iomanip>.)
In case you're wondering, the effect of showpos endures for subsequent
inserter calls, in either of the above forms. I'll show how to turn it off later.
But the field width evaporates after the first inserter that makes use of it.
cin You can probably guess what's coming next. The header <iostream>
also supports reading and decoding text from various streams, including
input files. It declares the object cin, which helps you extract characters
from the standard input stream, in cooperation with stdin. As you might
further guess, this object is of class istream, the obvious companion to
ostream.
extractors Thus, you can write code involving extractors, such as:
int n;
cin » n;
to read a sequence of characters and decode them by the usual rules for
encoded integer input. The "usual" rules are much as for the function
scanf:
• Skip any leading white space.
• Gobble one or more characters that look like a valid encoded integer and
convert them to int representation.
• If no such characters are found, or if the result can't be properly repre-
sented as type int, report a failure.
One small difference exists between inserters and extractors. You can
insert the value of an expression into a stream. (This is traditionally called
an "rvalue" by C programmers.) But you extract from a stream into an
object. (Those same C programmers would call this an "lvalue," but the
times and the terms they are a changing.) A corresponding difference
appears in C - you call printf with arbitrary expressions for value
arguments and you call scanf with pointer arguments to designate the
objects to store into.
In C++, you declare extractors with reference parameters, as in:
istream& istream::operator»(int& n);
106 Chapter 6
That ampersand lets you write a bald n, but still ensures that a real live
lvalue gets bound to the corresponding parameter within the function. No
worry about null pointers or other pointer type mismatches.
You can also play tricks with extractors, by the way, much like that endl
shorthand I showed earlier. If all you want to do, for example, is consume
any pending white space from the standard input stream, you can write:
cin » ws;
and the job is done. Similarly, you can communicate various bits of format-
ting information through other manipulators. much as with output
streams. Once again, I won't begin to explain the magic behind that bald
ws manipulator until later. (See page 214.) Just note for now that such tricks
are possible.
history Iostreams has several clear advantages over the formatted 110 functions
of the Standard C library: Little wonder that every implementation of c++
has for years offered some version of iostreams, however much the imple-
mentation may vary in its support for other common library classes. Jerry
Schwarz, now at Lucid Technolog)', gets credit for fleshing out an early
version of iostreams, for helping it become widespread, and for seeing the
package through more than one major revision. He is also responsible for
drafting the specification of iostreams in the draft Standard c++ library.
Unfortunatel)', little has been written on the detailed architecture of
iostreams. About the only commercially available guide is a book by Steve
Teale (Tea93), which deals with a slightly dated version of the package.
Since the draft C++ Standard progresses even beyond the current field
version, Teale's book offers only limited guidance. Still, it's better than what
you typically get from the vendor of a C++ translator.
extensibility For many class libraries this lack of information would not be a problem.
But iostreams is designed to be extensible in several important ways:
• You can overload operator« to define additional inserters, or opera-
tor> > to define additional extractors, for classes you define.
• You can define a host of manipulators that work with objects of class
istream, class ostream, or both.
• You can derive new classes from class streambuf, then override several
of its virtual member functions, to control sources and sinks of characters
of your own devising.
Such power is not without its complexity: And complexity can be mas-
tered safely only with careful guidance. To date, programmers have relied
on access to bits and pieces of library source code to get that guidance.
Where such code is not available, or where it varies among implementa-
tions, adequate guidance has been lacking. Standardizing iostreams is thus
a major step toward helping the package realize its full potential.
ios I begin with some basic architecture. Classes istream and ostream have
several requirements in common:
<ios> 107
• Both must control a stream through the agency of some object of class
streambuf.
• Both must maintain some notion of the state of the controlled stream,
including a history of any errors that have occurred and how to report
future errors.
• Both must memorize a host of formatting options, as I described earlier.
• Both must define a number of common types for describing the member
functions and objects needed to effectuate the above requirements.
<ios> To provide all these services, both classes derive from the virtual public
base class ics. And that is the topic of this chapter. This base class is
sufficiently large, and comes with enough related functions, that the draft
C++ Standard assigns it a header of its very own, called <ios>.
Note that class ios is a virtual base for both istream and ostream. That
is more a matter of compatibility with past practice than of necessity:
Existing implementations control a stream that can be both read and
written by declaring an object of class iostream, defined something like:
iostream class iostream
: public istream, ostream {
. .. .. };
Were the base ios not virtual, this class would end up with two such
subobjects, not just one. That would lead to all sorts of confusion in trying
to control the bidirectional stream. Making the base virtual adds a bit of
complexity here and there, particularly with initialization, but it permits
the traditional definition of class iostream for those who want to use it.
This class is not a part of the draft C++ Standard, however, because it is
no longer necessary: The preferred way to control a bidirectional stream is
with two separate objects, one of class istream and one of class ostream.
Both point to the same streambuf object, which is the only agent that really
has to know that the stream can be both read and written. (That is part of
the reason why an object of class ios contains a pointer to a separate
streambuf object, instead of the object itself. See page 122.)
coming Class ios lies at the heart of the iostreams machinery, so I deal with it
attractions first. You have to understand it in detail to make sense of its more visible
descendants, the classes istream and ostream. But it is far from the whole
story. That tale spreads out over the next eight chapters as well. Here is a
brief overview of what you can expect.
streambuf Class streambuf is equally fundamental to iostreams. (See Chapter 7:
<streambuf>.) You can get quite a lot of use out of iostreams without ever
declaring a streambuf object directly. But if you want to know how the
whole works hangs together, or if you want to extend iostreams in nontriv-
ial ways, you must know how this class behaves in detail.
manipulators Manipulators are yet another topic. Many are simple and easy to explain
istream once you know the basics of classes ios, istream (Chapter 8: <istream»,
ostream and ostream (Chapter 9: <ostream». But all those manipulators with
108 Chapter 6
arguments derive from one of several "interesting" template classes. (See
Chapter 10: <iomanip>.) Of course, in many existing implementations, they
are built atop even more interesting macros.
strstreambuf Two library classes show some of the power of class streambuf. Class
stringbuf strstreambuf provides capabilities akin to the Standard C library function
sprintf. (See Chapter 11: <strstream>.) You can use inserters and extrac-
tors to manipulate in-memory text strings. Class stringbuf is similar,
except that it eases conversion between such in-memory strings and objects
of the standard library class string. (See Chapter 12: <sstream>.) I discuss
class string after I've presented the last of iostreams. (See Chapter 15:
<string>.)
filebuf Finally, I return to the objects that manipulate external files. Class file-
cin buf lets you open files by name, much as with fopen, then manipulate them
cout as iostreams. (See Chapter 13: <fstream>.) And the objects cin, cout, cerr,
cerr and clog have their own tale as well. (See Chapter 14: <iostream>.) It turns
clog out that initializing these creatures, as required by the draft C++ Standard,
is no mean feat.
class As a quick overview, here are the two hierarchies that subsume most of
hierarchy the classes that make up iostreams. One is based on class ios, the topic of
this chapter. The other is based on class streambuf, which I cover in the
next chapter:
ios
istream streambuf
istrstream strstreambuf
istringstream stringbuf
ifstream filebuf
istdiostream stdiobuf
ostream
ostrstream
ostringstream
ofstream
ostdiostream
The description of the iostreams facilities occupies about half the library
portion of the Informat Review Draft. It's going to take quite some time to
cover it in adequate detail.
The class ios serves as a base class for the classes istream and ostream. It
defines several member types:
• a class failure derived from xmsg;
• a class Init;
• three bitmask types, fmtflags, iostate, and openmode;
• an enumerated type, seekdir.
It maintains several kinds of data:
• a pointer to a stream buffel~ an object of class streambuf, that controls
sources (input) and sinks (output) of character sequences;
• state information that reflects the integrity of the stream buffer;
• control information that influences how to interpret (format) input sequences
and how to generate (format) output sequences;
• additional information that is stored by the program for its private use.
For the sake of exposition, the maintained data is presented here as:
• streambuf* sb, points to the stream buffer;
• ostream* tiestr, points to an output sequence that is tied to (synchronized
with) an input sequence controlled by the stream buffer;
• iostate state, holds the control state of the stream buffer;
• iostate except, holds a mask that determines what elements set in state
cause exceptions to be thrown;
• fmtflags fmtfl, holds fonnat control information for both input and
output;
• int wide, specifies the field width (number of characters) to generate on
certain output conversions;
• int prec, specifies the precision (number of digits after the decimal point)
to generate on certain output conversions;
• char fillcb, specifies the character to use to pad (fill) an output conversion
to the specified field width;
• static int index, specifies the next available unique index for the integer
or pointer arrays maintained for the private use of the program, initialized to
an unspecified value;
• int* iarray, points to the first element of an arbitrary-length integer array
maintained for the private use of the program;
• void** parray, points to the first element of an arbitrary-length pointer
array maintained for the private use of the program.
17.4.1.1.1 Class ios: : failure
failure class failure : public xmsg (
public:
failure(const char* where_arg = 0, const char* why_srg = 0);
virtual -failure();
protected:
II virtual void do_raise(); inherited
};
112 Chapter 6
The class failure defines the base class for the types of all objects thrown as
exceptions, by functions in the Standard c++ library, to report errors detected
during stream buffer operations.
17.4.1.1.1.1 ios: :failure: : failure (const char* , const char*)
constructor failure(const char* wbere_arg = 0, const char* wby_srg = O};
Constructs an object of class failure, initializing the base class with
xmsg(wnst_srg, wnere_srg, why_srg) , where the NTMBS pointed to
by wnst_srg is unspecified.
17.4.1.1.1.2 ios: : failure: :-failure()
destructor virtual -failure();
Destroys an object of class failure.
17.4.1.1.1.3 ios: : failure: : do_raise ()
II virtual void do_raise(); inherited
Behaves the same as xmsg: : do_raise ( ) .
17.4.1.1.2 Type ios:: fmtflags
fmtflags typedef Tl fmtflags;
The type fmtflags is a bitmask type (indicated here as Tl) with the elements:
• dec, set to convert integer input or to generate integer output in decimal base;
• fixed, set to generate floating-point output in fixed-point notation;
• hex, set to convert integer input or to generate integer output in hexadecimal
base;
• internal, set to add fill characters at a designated internal point in certain
generated output;
• left, set to add fill characters on the right (final positions) of certain
generated output;
• oct, set to convert integer input or to generate integer output in octal base;
• right, set to add fill characters on the left (initial positions) of certain
generated output;
• scientific, set to generate floating-point output in scientific notation;
• showbase, set to generate a prefix indicating the numeric base of generated
integer output;
• showpoint, set to generate a decimal-point character unconditionally in
generated floating-point output;
• showpos, set to generate a + sign in non-negative generated numeric output;
• skipws, set to skip leading white space before certain input operations;
• unitbuf, set to flush output after each output operation;
• uppercase, set to replace certain lowercase letters with their uppercase
equivalents in generated output.
Type fmtflags also defines the constants:
• adjust field, the value left I right I internal;
• basefield, the value dec I oct I hex;
• floatfield, the value scientific I fixed.
17.4.1.1.3 Type ios: : iostate
iostate typedef T2 iostate;
The type iostate is a bitmask type (indicated here as T2) with the elements:
• badbit, set to indicate a loss of integrity in an input or output sequence (such
as an irrecoverable read error from a file);
<ios> 113
• eofbit, set to indicate that an input operation reached the end of an input
sequence;
• failbi t, set to indicate that an input operation failed to read the expected
characters, or that an output operation failed to generate the desired characters.
Type iostate also defines the constant:
• goodbit, the value zero.
17.4.1.1.4 Type ios: :openmode
opemnode typedef T3 openmode;
The type opemnode is a bitmask type (indicated here as 213) with the elements:
• app, set to seek to end-of-file before each write to the file;
• ate, set to open a file and seek to end-of-file immediately after opening the
file;
• binary, set to perform input and output in binary mode (as opposed to text
mode);
• in, set to open a file for input;
• out, set to open a file for output;
• trunc, set to truncate an existing file when opening it.
17.4.1.1.5 Type ios:: seekdir
seekdir typedef T4 seekdir;
The type seekdir is an enumerated type (indicated here as 214) with the
elements:
• beg, to request a seek (positioning for subsequent input or output within a
sequence) relative to the beginning of the stream;
• cur, to request a seek relative to the current position within the sequence;
• end, to request a seek relative to the current end of the sequence.
17.4.1.1.6 Type ios:: io_state
II typedef T5 io_state; optional
The type io_state is a synonym for an integer type (indicated here as T5) that
permits certain member functions to overload others on parameters of type
iostate and provide the same behavior.
17.4.1.1.7 Type ios:: open_mode
II typedef T6 open_mode; optional
The type open_mode is a synonym for an integer type (indicated here as 216)
that permits certain member functions to overload others on parameters of type
opemnode and provide the same behavior.
17.4.1.1.8 Type ios:: seek_dir
II typedef T7 seek_dir; optional
The type seek_dir is a synonym for an integer type (indicated here as 2'7) that
permits certain member functions to overload others on parameters of type
iostate and provide the same behavior.
17.4.1.1.9 Class ios: : ini t
Class class :Init {
ios: : init public:
:Init();
-:Init();
private:
114 Chapter 6
1/ static int init_cnt; exposition only
};
The class J:nit describes an object whose construction ensures the construction
of the four objects declared in <iostream> that associate file stream buffers
with the standard C streams provided for by the functions declared in
< stdio • h>. For the sake of exposition, the maintained data is presented here
as:
• static int init_cnt, counts the number of constructor and destructor
calls for class J:nit, initialized to zero.
17.4.1.1.9.1 ios: :Init: :J:nit()
constructor Init();
Constructs an object of class Init. If init_cnt is zero, the function stores the
value one in init_cnt, then constructs and initializes the four objects cin
(17.4.9.1), cout (17.4.9.2), cerr (17.4.9.3), and clog (17.4.9.4). In any case,
the function then adds one to the value stored in init_cnt.
17.4.1.1.9.2 ios: :Init: :-Init()
destructor ... Init();
Destroys an object of class J:nit. The function subtracts one from the value
stored in init_cnt and, if the resulting stored value is one, calls
cout. flush(), cerr.flush(), and clog. flush ( ).
17.4.1.1.10 ios: :ios(streambuf'*)
constructor ios(streambuf* sb_arg);
Constructs an object of class ios, assigning initial values to its member objects
by calling init (sb_arg) .
17.4.1.1.11 ios: : - ios ( )
destructor virtual -ios();
Destroys an object of class ios.
17.4.1.1.12 ios: : operator void'* ( )
operator void*() const
operator Returns a non-null pointer (whose value is otherwise unspecified) if failbit
void'* I badbit is set in state.
17.4.1.1.13 ios: : operator I ()
operatorl int operatorl() const
Returns a nonzero value if failbit I badbit is set in state.
17.4.1.1.14 ios: : copyfmt (const ios&:)
copyfmt ios& copyfmt(const 10s& rns);
Assigns to the member objects of '*this the corresponding member objects of
rhs, except that:
• sb and state are left unchanged;
• except is altered last by calling exception ( rns • except) .
If any newly stored pointer values in "'tnis point at objects stored outside the
object rns, and those objects are destroyed when rns is destroyed, the newly
stored pointer values are altered to point at newly constructed copies of the
objects.
The function returns '*this.
17.4.1.1.15 ios: :tie ()
tie ostream* tie() const;
<ios> 115
Returns tiestr.
17.4.1.1.16 ios: :tie(ostream*)
tie ostream* tie(ostream* tiestr_srg);
Assigns tiestr_argto tiestr and then returns the previous value stored in
tiestr.
17.4.1.1.17 ios: : rdbuf ( )
rdbuf streambuf* rdbuf() const;
Returns sb.
17.4.1.1.18 ios: :rdbuf (streambuf*)
rdbuf streambuf* rdbuf(streambuf* sb_srg);
Assigns sb_srg to sb, then calls clear ( ). The function returns the previous
value stored in sb.
17.4.1.1.19 ios: :rdstate()
rdstate iostate rdstate() const;
Returns state.
17.4.1.1.20 ios: : clear (iostate)
clear void clear(iostate state_srg = goodbit);
Assigns state_arg to state. If sb is a null pointer, the function then sets
bad.bit in state. If state & except is zero, the function returns. Otherwise,
the function calls fail. raise () for an object fail of class failure,
constructed with argument values that are implementation-defined.
17.4.1.1.21 ios: : clear (io_state)
clear II void clear(io_state state_arg); optional
Calls clear( (iostate) state_arg).
17.4.1.1.22 ios: :setstate(iostate)
setstate void setstate(iostate state_arg);
Calls clear(state I state_arg).
17.4.1.1.23 ios: : setstate (io_state)
setstate II void setstate(io_state stste_arg); optional
Calls clear ( (iostate) (state I state_arg».
17.4.1.1.24 ios: : good ( )
good int good() const;
Returns a nonzero value if state is zero.
17.4.1.1.25 ios: : eof ()
eof int eof() const;
Returns a nonzero value if eofbit is set in state.
17.4.1.1.26 ios: : fail ( )
fail int fail() const;
Returns a nonzero value ifbadbit or failbit is set in state.
17.4.1.1.27 ios: :bad()
bad int bad() const;
Returns a nonzero value ifbadbit is set in state.
116 Chapter 6
17.4.1.1.28 ios: : exceptions ( )
exceptions iostate exceptions() const;
Returns except.
17.4.1.1.29 ios: : exceptions (iostate)
exceptions void exceptions(iostate except_arg);
Assigns except_srgto except, then calls c1ear{state).
17.4.1.1.30 ios: : exceptions (io_state)
exceptions II void exceptions(io_state except_srg); optional
Calls except ions ( (iostate) except_srg) .
17.4.1.1.31 ios: :f1ags()
flags fmtflags flags() const;
Returns fmtfl.
17.4.1.1.32 ios: :f1ags (fmtf1ags)
flags fmtflags flags(fmtflags fmtfl_srg);
Assigns fmtfl_srg to fmtfl and then returns the previous value stored in
fmtfl.
17.4.1.1.33 ios: :setf(fmtf1ags)
setf fmtflags setf(fmtflags fmtfl_srg);
Sets fmtfl_srgin fmtfl and then returns the previous value stored in fmtfl.
17.4.1.1.34 ios: :setf(fmtflags, fmtflags)
setf fmtflags setf(fmtflags fmtfl_srg, fmtflags mask);
Clears mask in fmtfl, sets fmtfl_srg &: mask in fmtfl, and then returns
the previous value stored in fmtfl.
17.4.1.1.35 ios: :unsetf(fmtf1ags)
unsetf void unsetf(fmtflags mask);
Clears mask in fmtfl.
17.4.1.1.36 ios: :fi11()
fill int fill() const;
Returns fill.
17.4.1.1.37 ios: :fil1(int)
fill int fill(int fillcn_srg);
Assigns fillch_srgto fillch and then returns the previous value stored in
fillch.
17.4.1.1.38 ios: :preci sion ( )
precision int precision() const;
Returns prec.
17.4.1.1.39 ios: :precision(int)
precision int precision(int prec_arg);
Assigns prec_srg to prec and then returns the previous value stored in prec.
17.4.1.1.40 ios: :width ( )
width int width() const;
Returns wide.
<ios> 117
17.4.1.1.41 ios: :width(int)
width int width(int wide_srg);
Assigns wide_argto wide and then returns the previous value stored in wide.
17.4.1.1.42 ios: : xal10c ( )
xalloc static int xalloc();
Returns index++.
17.4.1.1.43 ios: : iword(int)
iword long& iword(int idx);
If iarray is a null pointer, allocates an array of int of unspecified size and
stores a pointer to its first element in iarray. The function then extends the
array pointed at by iarrayas necessary to include the element iarray[ idx] .
Each newly allocated element of the array is initialized to zero. The function
returns iarray[idx]. After a subsequent call to iword(int) for the same
object, the earlier return value may no longer be valid. 84)
17.4.1.1.44 ios: : pword (int )
pword void* & pword(int idx);
If parray is a null pointer, allocates an array of pointers to void of unspecified
size and stores a pointer to its fITst element in parray. The function then extends
the array pointed at by parray as necessary to include the element par-
ray[idx]. Each newly allocated element of the array is initialized to a null
pointer. The function returns parray[idx]. After a subsequent call to
pword (int) for the same object, the earlier return value may no longer be valid.
17.4.1.1.45 ios: : ios ( )
constructor ios();
Constructs an object of class ios, assigning initial values to its member objects
by calling init ( 0 ) .
17.4.1.1.46 ios: : init (streambuf*)
init void init(streambuf* sb_srg);
Assigns:
• sb_arg to sb;
• a null pointer to tiestr;
• goodbit to state if sb_arg is not a null pointer, otherwise badbit to
state;
• goodbit to except;
• skipws I dec to fmtfl;
• zero to wide;
• 6 to prec;
• the space character to fillch;
• a null pointer to iarray;
• a null pointer to parray.
17.4.1.2 dec (ios&:)
dec ios& dec(ios& str);
Calls str. setf (ios: :dec, ios: :basefield) and then returns str. 85 )
17.4.1.3 fixed(ios&:)
fixed ios& fixed(ios& str);
Calls str. setf (ios: : fixed, ios:: floatfield) and then returns str.
118 Chapter 6
17.4.1.4 hex(ios&:}
hex ios& hex(ios& str);
Calls str. setf (ios : : hex, ios:: basefie1d) and then returns str.
17.4.1.5 internal (ios&:)
internal ios& internal(ios& str);
Calls str. setf (ios:: internal, ios: :adjustfie1d) and then returns
str.
17.4.1.6 left (ios&:)
left ios& left(ios& str);
Calls str. setf (ios: : left, ios: :adjustfie1d) and then returns str.
17.4.1.7 noshowbase (ios&:)
noshowbase ios& noshowbase(ios& str);
Calls str.unsetf (ios:: showbase) and then returns str.
17.4.1.8 noshowpoint ( ios&:)
noshowpoint ios& noshowpoint(ios& str);
Calls str. unsetf (ios : : showpoint) and then returns str.
17.4.1.9 noshowpos ( ios&: )
noshowpos ios& noshowpos(ios& str);
Calls str. unsetf (ios : : showpos) and then returns str.
17.4.1.10 noskipws (ios&:)
noskipws ios& noskipws(ios& str);
Calls str. unsetf (ios : : skipws) and then returns str.
17.4.1.11 nouppercase ( ios&: )
nouppercase ios& nouppercase(ios& str);
Calls str. unsetf (ios : : uppercase) and then returns str.
17.4.1.12 oct ( ios&: )
oct ios& oct(ios& str);
Calls str. setf (ios: : oct, ios: :basefie1d) and then returns str.
17.4.1.13 right (ios&:)
right ios& right(ios& str);
Calls str. setf (ios: : right, ios: :adjustfie1d) and then returns str.
17.4.1.14 scientific (ios&:)
scientific ios& scientific(ios& str);
Calls str. setf (ios: : scientific, ios:: floatfie1d) and then returns
str.
17.4.1.15 showbase (ios&:)
showbase ios& showbase(ios& str);
Calls str. setf (ios : : showbase) and then returns str.
17.4.1.16 showpoint (ios&:)
showpoint ios& showpoint(ios& str);
Calls str. setf (ios: : showpoint) and then returns str.
17.4.1.17 showpos ( ios&:)
showpos ios& showpos(ios& str);
<ioe> 119
Calls str. setf (ios : : showpos) and then returns str.
17.4.1.18 skipws (ios&)
skipws ios& skipws(ios& str);
Calls str. setf (ios : : skipws) and then returns str.
17.4.1.19 uppercase ( ios& )
uppercase ios& uppercase(ios& str);
Calls str. setf (ios : : uppercase) and then returns str.
Footnotes:
Footnotes 84) An implementation is free to implement both the integer array pointed at
by iarray and the pointer array pointed at by parray as sparse data
structures, possibly with a one-element cache for each.
85) The function signature dec (ios&) can be called by the function signature
ostream& stream::operator«(ostream& (*)(ostream&»
to permit expressions of the form cout << dec to change the format flags
stored in couto
Future Directions
optional As I mentioned earlier, the Committee has changed the status of the
members "optional members" that use the older types ios:: io_state, ios::
open_mode, and ios: : seek_dire (See page 16.) These are now "deprecated
features." There's no harm, and much good, in continuing to provide them
as part of a Standard C++ librar}T, however.
locale As I also mentioned earlier, the Committee has added locale objects as
objects well. (See page 32.) You can now imbue a stream with a locale object, which
encapsulates some locale. Inserters and extractors operating on that stream
follow the conventions specified by the imbued locale. The visible effect on
class ios is the addition of member functions for imbuing a locale object
and later accessing it to determine locale-specific behaviors.
wide-character Far more ambitious is a change that replaces all of the iostreams machin-
streams ery with templates. The idea is to ease the introduction of "wide-character
streams." These are streams that traffic in wide characters (elements of type
wchar_t), rather than the traditional single-byte characters (elements of
type char). Cultures that use very large character sets - such as in Japan
and China - are a growing force in both commercial programming and
international programming-language standards. The people who serve
these rapidly growing markets naturally want the same power that has
been available to cultures with smaller character sets. In this case, the drive
is toward providing all of iostreams in parallel versions, for both single-byte
and wide characters (and for possibly other "characters" as well).
Redefining iostreams in terms of templates has certain advantages. The
draft C++ Standard can describe the common properties of all flavors of
iostreams just once. (By contrast, see Chapter 15: <string> and Chapter 16:
<wstring>.) The relatively few differences are then easier to summarize
and highlight. Moreover, templates are open ended. The Standard C++
120 Chapter 6
library may be obliged to provide instantiations only for single-byte and
wide characters, but programmers are free to instantiate more. That can
prove to be a boon should unforeseen uses arise.
template On the other hand, templates pose certain problems as well. The current
limitations state of the art does not encourage ambitious use of templates. Existing
implementations favor different organizations of source files (still within
the scope of the draft C++ Standard). It can be a challenge to write a
nontrivial template that works unchanged with multiple C++ translators.
Some of those implementations also suffer surprising capacity limitations
when processing templates. Experienced C++ programmers know to keep
templates small and simple, for now, if they hope to move them about freely.
Naturally, all such limitations should disappear over time. The draft C++
Standard has added several new features to the language, including excep-
tions, templates, runtime type identification (RTTI), and namespace decla-
rations. The Standard C++ library cannot ignore these language additions
simply because current support is weak. It is a delicate balancing act to
specify a Standard c++ library that can be implemented adequately in the
short term, but that uses the full language in the long term.
functional Please note, however, that the basic functionality of traditional (single-
compatibility byte) iostreams does not change with the introduction of templates. A
program written to match the Informal Review Draft should remain essen-
tially unchanged. Only a (very new) program that uses, sa~ wide-character
streams will notice the lack of iostreams templates. So even with all the
machinery of iostreams officially replaced by templates, the lessons of this
and subsequent chapters still prove useful. So, in fact, is the code presented
here. Think of it as an efficient specialization of iostreams for type char.
Using <ios>
You have little or no occasion to include the header <ios> directly: It is,
in fact, one of a handful of headers invented by the Committee simply to
split the iostreams declarations into more manageable pieces. But the
chances are very good that you'll use the contents of <ioe> in a program.
Class ios is the base class for both istream and ostream, which mediate
practically all input and output in the iostreams package. The base class
summarizes what is common to both input and output. And the functions
defined in this header, which operate on objects of class ios, are sometimes
useful both for input and output.
member Class ios defines several member types. Some are used directly within
types the class, others are not. But all are involved in the operation of iostreams.
Packing this assortment of types within class ios is an attempt, from the
earliest days of iostreams, to reduce the clutter of names.
Here is the first use made of two flavors of types introduced in the "front
matter" of the library portion of the draft c++ Standard (see page 11):
<ios> 121
enumerated - An enumerated type (17.1.5.10.1) is an IJhonest" enumeration. It defines
types one or more enumerated elements, which are all distinct values. You assign
such elements only to an object of the proper enumerated type, and you
compare such stored values only for equality with these elements.
bitmask - A bitmask type (17.1.5.10.2) is an enumerated type with the bitwise
types operations (such as AND, OR, and EXCLUSIVE OR) defined for it. It
defines one or more bitmask elements, whose values typically each have
just one distinct bit set. It may also define bitmask constants, whose values
are the union of zero or more bitmask elements. You perform bitwise
operations only between values of the same bitmask type.
The draft C++ Standard introduces these definitions for several reasons.
One is to give implementors some latitude in how they specify such types.
Nobody wants to have to slavishly duplicate all those static const
declarations. (See, for example, page 109.) An anum is much tidier. On the
other hand, anum doesn't tell the whole story. So the draft C++ Standard
defines a term that has additional constraints.
enumeration The Committee has added the ability to overload on enumerations. Now
overloading you can, and must, write a passel of functions to define the bitwise opera-
tions, as the draft C++ Standard shows. Otherwise, you'll find yourself
writing lots of (int) type casts, to combine bitmask values, and even more
type casts to bless the int results before storing them in bitmask objects.
All of this machinery is designed to improve the type safety of programs
that fiddle bits. Too often, innocent programmers confuse two flavors of
bitmask elements and mix them inappropriately in an expression. The
resulting mishmash goes undiagnosed if all the operands have type into
Subsequent debugging is a pain.
optional Nevertheless, the draft C++ Standard permits an implementation to
types retain support for several of the older types as well. I list them below, for
completeness, but I suggest you avoid using them. I also suggest you
exercise caution in using the new enumerated and bitmask types. Not all
implementations support overloading on enumerations yet. The type
safety promised by the draft C++ Standard can't always be delivered. But
get in the habit of pretending that the stronger type checking is there.
Otherwise, you'll have lots of code to rewrite later.
The member types in class ios are:
failure - ios:: failure - an exception class that describes all objects thrown as
exceptions by the Standard C++ library to report iostreams problems
fmtflags - ios:: fmtflags - a bitmask type that specifies combinations of format-
ting attributes for input and/or output
iostate - ios:: iostate - a bitmask type that specifies combinations of status
indications for an associated streambuf object
openmode - ios:: openmode - a bitmask type that specifies combinations of quali-
fiers when opening a file
122 Chapter 6
seekdir - ios:: seekdir - an enumerated type that specifies what kind of posi-
tioning operation to perform on a streambuf object
io_state - ios:: io_state - the precursor to iostate, retained for backward
compatibility
open_mode • ios : : open_mode - the precursor to openmode, retained for backward
compatibility
seek_dir - ios:: seek_dir - the precursor to seekdir, retained for backward
compatibility
I write all these nested types with the ios: : access qualifier as a reminder.
Typicall)', you must write the fully qualified name for any use you make of
these types in a program. It doesn't hurt to make a habit of doing so.
stream Perhaps the single most important role for an object of class ios is to
buffers keep tabs on an associated object of class streambuf, also known as a stream
buffer. All the actual inserting and extracting of character sequences occurs
under control of a stream buffer. An ios object merely takes notes. It
remembers formatting preferences for objects (typically of the derived
classes istream or ostream) that convert between these character se-
quences and values of various types within a program. And it remembers
the status of earlier operations on the stream buffer. I deal with the latter
first.
rdbuf The member function rdbuf () returns the stored pointer to an object of
class streambuf. The pointer can be null. Often, the pointer value is stored
when the ios object is first constructed and never changes afterward. But
you can also store a different pointer in an ios object x by calling
x. rdbuf (sb) , or even x. rdbuf (0). Yes, the name is a misnomer, since the
rd suggests that you can only "read" the stored pointer.
One reason to call rdbuf is to set up a bidirectional stream. (See page
107.) You might write something like:
#include <fstream>
ifstream istr("abc", ios::in I ios::out);
ostream ostr(istr.rdbuf(»;
This sequence constructs two objects. istr has a type derived from class
istream (and hence from ios). This particular constructor opens the file
"abc", places it under control of a stream buffer, and then points at that
stream buffer (if the open was successful). The stream buffer is a member
object within istr. ostr is a generic ostream object (also derived from
ios). Here, it is constructed with a specific pointer to streambuf argument.
The expression istr. rdbuf () supplies the pointer to the stream buffer to
be shared between the two objects.
Equall)T, you might write something like:
#include <fstream>
ifstream istr(lIabc n , ios::in I ios::out);
ostream ostr;
ostr.rdbuf(istr.rdbuf(»;
<ioe> 123
to achieve the same effect with somewhat less grace.
The only other time you have occasion to use rdbuf is to access stream-
buf member functions directl)'. (See Chapter 7: <streambuf>.) You can do
quite a lot with iostreams, however, without descending to this level.
An operation on a stream buffer that fails sets one or more status bits
within an ios object. These status bits inhabit a member object of type
iostate. Call:
clear. x.clear() to clear all status bits
clear • x.clear (new_stat) to reset the status bits to the value new_stat (yes,
it's another misnomer)
setstate • setstate (mask) to set the status bits in mask without clearing other
status bits
rdstate Other member functions let you read and test the status bits in various
ways. The simplest call is x. rdstate ( ) , which returns all the status bits.
The remaining member functions of this ilk are more specific:
good. good () (same as rdstate () == (iostate) 0) indicates no errors
bad. bad () (same as rdstate () &: ios: :badbit) indicates a null stream-
buffer pointer, a read/write error, or some other loss of integrity of the
associated stream buffer
eof • eof () (same as rdstate () &: ios: :eofbit) indicates that an earlier
extraction operation has encountered the end of the input stream
fail· fail() (same as rdstate() &: (ios::badbit I ios::failbit»in-
dicates that an earlier extraction operation has failed to match the
required pattern of input text
ipfx Any functions you write to manipulate an istream or ostream object
opfx should be careful to test the status bits before trying anything rash. You
don't want to reach for a stream buffer that isn't there, for example.
Typically this service is provided for you by the functions istream: : ipfx
(page 196) or ostream: : opfx (page 238). If you get more deeply involved
in manipulating objects based on class ios, however, you may have occa-
sion to call the above member functions directly.
exception What if you don't want your program simply to keep stumbling on in
the presence of iostream failures? Or what if you don't want to test the result
of every operation that calls in tum on iostreams functions? That's where
the newly added exception mask comes in. An ios object contains a second
member object of type iostate, in addition to the status bits. You can obtain
its stored value by calling x.exception(), or set it by calling x.excep-
tion (iostate) . Any change ofstatus, or of the exception mask, that results
in x. rdstate () &: x. exceptions () becoming nonzero throws an excep-
tion of class ios: : failure. (As usual, the "throw" of the exception object
ex is actually the call ex. raise ( ) .)
Initiall)', the exception mask is zero for any object of class ios. If you
don't believe in exceptions, or don't want to be bothered by them here, you
can simply ignore this machine!)'.
124 Chapter 6
operator I Class ios has one other bit of cuteness with regard to testing its status
void * flags. It overloads the unary operator 1 () and the unary type cast opera-
tor void * () as a quick way of testing whether either of the status bits
ios: : failbit or ios: :badbit is nonzero. Thus, you can write:
if (l(cin » obj»
cerr « "can't read obj" « endl;
because the type of cin is derived from class ios. Be warned, however, that
the traditional effect of some tests like this is not always the same as for the
draft C++ Standard. Check your old code carefully when upgrading it to
conform.
formatting The second most important role for class ios, after keeping tabs on a
information stream buffer, is remembering formatting information. The member func-
tions that do so are mostly trivial. Quite a few, in fact, merely store a new
value in one of the private member objects, then return the previously
stored value. Many others just return the stored value. It is up to the derived
classes istream, ostream, and their kin to actually do something with all
this information.
manipulators You alter or access formatting information directly by calling member
functions for objects of class ios. A more indirect approach is to invoke
manipulators. These exploit clever member functions in classes derived from
ios. Class istream, for example, defines the extractor member function:
istream& qperator»(ios& (*_F)(ios&»
{(*_F) (*(ios *)this); return (*this); }
which interacts neatly with a declaration such as:
ios& dec(ios&);
The net effect is that, for an istream object such as cin, you can write:
cin » dec;
and obtain the same effect as the call:
dec (cin);
It takes a bit of study to see why this is true, but the time is well repaid.
Naturally, class ostream defines a similar inserter member function to
support the same clever notation. Some manipulators work only with
extractors, some only with inserters, and some with both. The ones that
work with both are declared in <ios> if they require no additional argu-
ments. Otherwise, they are declared in <iomanip>.
Here is a summary of the stored values used as formatting information.
I describe each in terms of the member function that returns the value. Far
more often, you call a member function of the same name, but with a
different function signature, to store a new value. But that is not necessarily
the best way to do so. The manipulators described later in this chapter, and
in Chapter 10: <iomanip>, are often more convenient.
f i l l . fill () - returns the fill character that some inserters use to pad to a
setfill specified field width. fill (int) stores a new value, but the inserter
setfill (int) (17.4.5.4.4) is often handier.
<ios> 125
precision - precision () - returns the precision value that floating-point inserters
setprecision use to determine the number of digits to insert. precision (int) stores
a new value, but the inserter setprecision(int) (17.4.5.4.5) is often
handier.
width - width () - returns the width value that the extractor istream&: opera-
setw tor» (char*) uses to determine the maximum number of characters to
extract, and that many inserters use to determine the minimum number
of characters to insert. An extractor or inserter that makes use of the
width invariably sets it to zero - this is the one piece of formatting
information that is transient. width (int) stores a new value, but the
inserter setw (int) (17.4.5.4.6) is often handier.
xalloc - xalloc () - returns an index, suitable for use with iword or pword
below, that is unique among all ios objects during a given program
execution. This machinery lets you store in an ios object some integer
or object pointer values used only by inserters or extractors you supply.
This extensibility is handy when simply recycling the fill character,
precision, and/ or width doesn't do the job. Typically, you call xalloc
once for each unique index you need, and store the index in a static
object. The functions that need to access the values stored with the ios
object then use this index to select the appropriate elements. (If you don't
understand why you might need this capabili~ you're probably not
ready to indulge in such antics.)
iword - iword(int n) - returns a reference to element n, of type long, of an
extensible sequence peculiar to each ios object. Each element is initial-
ized to zero. The reference remains valid, even for storing a new value,
until the next call to either iword or pword for the ios object. The
last-stored values of all elements are retained for the lifetime of the ios
object and are copied as part of its value.
pword - pword (int n) - returns a reference to element n, of type pointer to void,
of an extensible sequence peculiar to each ios object. Otherwise, its
behavior is the same as for iword, above.
tie - tie () - returns a pointer to an ostream object that is tied to the ios
object and is "flushed" (synchronized with any external files) before any
insertion or extraction controlled by the ios object. tie (ostream *)
stores a new value. You are likely to use this machinery only on those
rare occasions when you set up your own interactive files. (See Chapter
14: <iostream> for the commonest use of tieing.)
flags - flags () - returns the format flags that many extractors and inserters
use to qualify their behavior.
Format flags make a significant topic in its own right. Most flags (which
are bitmask elements) act independently, but some belong to one of three
groups that are always considered together. The flag skipws affects only
extractors. Nearly all other flags affect only inserters, but one group (base-
field) affects both. And essentially all flags are defined precisely in terms
of conversion specifications for the Standard C library functions fprintf or
126 Chapter 6
fscanf, declared in <stdio .h>. (See P&B92 for a readable summary of the
conversion specifications.) The flags, and their groupings, are:
adjustfie1d - adjustfie1d is the union of the flags internal, left, and right. The
internal width value can call for an inserter to generate additional fill characters,
left besides the minimum character sequence required to represent the
right inserted value. This group determines whether fill characters are in-
serted at some specified internal point in the character sequence (inter-
nal), before the character sequence (right), or after the character se-
quence (any other flag settings, but preferably left for clarity).
basefie1d - basefie1d is the union of the flags dec, hex, and oct. Inserters and
dec extractors can convert integer values with a variety of assumed numeric
hex bases. This group determines whether the base is 16 (hex), 8 (oct), or
oct decimal (any other flag settings, but preferably dec for clarity). More
precisely, an integer extractor takes a zero value for these three flags as
special license to determine the base from the form of the input field, the
same as the i conversion specifier does for fscanf.
f1oatfie1d - f1oatfie1d is the union of the flags fixed and scientific. Inserters
fixed can convert floating-point values in several formats, which are best
scientific characterized by the analogous conversion specifiers for fprintf. This
group determines whether the behavior is the same as for a conversion
specifier of f (fixed), e (scientific), or g (any other flag settings).
showbase - showbase determines whether an integer inserter generates the prefix
Ox or ox for base 16 conversion, or 0 for base 8 conversion (the same as
the # conversion qualifier for fprintf).
showpoint - showpoint determines whether a floating-point inserter generates a
trailing decimal point that would otherwise be suppressed (the same as
the # conversion qualifier for fprintf). The Standard C library function
loca1econv, declared in <locale. h>, defines what constitutes the deci-
mal point for the current locale.
showpos - showpos determines whether an integer or floating-point inserter gen-
erates a + before a non-negative value (the same as the + conversion
qualifier for fprintf).
skipws - skipws determines whether most extractors first extract and discard all
white-space characters before extracting the characters to convert. The
Standard C library function isspace, declared in <ctype.h>, defines
what constitutes white space for the current locale.
unitbuf - unitbuf determines whether an inserter flushes the ostream object
upon completion. You set this flag for a stream whose associated output
file should be kept in close synchronization with the program, such as
an interactive device or a debugging trace file. Otherwise, you should
leave this flag reset and favor the often dramatic improvement in per-
formance that comes with more aggressive buffering of output streams.
uppercase - uppercase determines whether an integer or floating-point inserter
generates any letters as uppercase instead of lowercase (the same as an
uppercase conversion specifier for fprintf).
<ios> 127
flags Class ios provides a variety of member functions to manipulate format
setf flags. You can replace all flags with flags (fmtf1ags), set selected flags
unsetf with setf(fmtf1ags), clear selected flags with unsetf(fmtf1ags), and
set selected flags under a mask with setf (fmt flags, fmtf1ags). The last
member function is useful for setting one flag in a group and clearing the
rest in that group, as in setf (dec, basefie1d).
resetiosf1ags Once again, you may find a manipulator handier for fiddling with
setbase format flags. You can set selected flags with setiosf1ags (ios: : fmt-
setiosf1ags flags) or clear selected flags with resetios flags ( ios: : fmt flags). To
set one of the common bases (8, 10, or 16), use setbase (int). Any other
value for base clears all flags in this group, resulting in adaptive extraction
or decimal (base 10) insertion. All these manipulators are declared in
<iomanip>. (See Chapter 10: <iomanip>.)
more The header <ios> also declares a gazillion manipulators that set and
manipulators clear format flags, singly and in groups:
internal left right
dec hex oct
fixed scientific
noshowbase showbase
noshowpoint showpoint
noshowpos showpos
noskipws skipws
nouppercase uppercase
While the set isn't complete, it does meet the commonest needs.
A good style for manipulating formatting information is to leave the
most-used values, and all flags, in their default state most of the time:
format flags are skipws I dec, width is zero, precision is 6, and fill
character is space. The width reverts to zero as soon as it's used. The others
you should revert before you forget.
copyfmt One handy addition made by the Committee eases saving and restoring
all the formatting information at once. The member function copy-
fmt (const ios&:) copies everything but the pointer to stream buffer and
the status bits from one ios object to another. Thus, you might write:
const static ios defau1t_ios;
ios save;
save.copyfmt(stdout); II save old info
stdout.copyfmt(defau1t_ios); II revert to defaults
stdout « setbase(16) « showbase « uppercase;
II muck with stdout freely
stdout.copyfmt(save}; II restore old info
Here is probably the only occasion you will have to declare objects of class
ioe not in captivity:
A small warning, however. The expression x. copyfmt (y) also copies
the exception mask to x. Its last act before returning is to test whether
x. rdstate () &: x. exceptions () is nonzero. If so, the function throws an
ios: : failure exception. Even if you plan for such exceptions, the timing
here can be surprising.
128 Chapter 6
init As a final note, class ios is more public than most about how it handles
:Init initializations. The protected member function init initializes an ios
subobject in a base class. Constructing an object of class ios: : :Init ensures
that the standard streams (cin, etc.) are properly initialized. Chances are
slim that you will have need to calion either of these mechanisms explicitly.
I suggest you resist the temptation to play with them. (1 show how the
Stndard C++ library uses them on your behalf in this and subsequent
chapters.)
Implementing <ios>
ios Figure 6.1 shows the file ios, which implements the standard header
<ios>. Much of it follows directly from the requirements of the draft C++
Standard. But it also includes a number of mysteries required by this
particular implementation.
_CATCH_IO_ The draft C++ Standard dictates how inserters and extractors should
EXCEPTIONS handle exceptions. (See Chapter 8: <istream> and Chapter 9: <ostream>.)
1anticipate, however, that not everybody will want the added overhead of
exception handling, particularly in every inserter and extractor. Thus, the
internal header <yxvals .h> must define the macro _CATCH_IO_EXCEP-
T:IONS to tum on exception handling within iostreams.
_TRY_:IO_BEGIN Even if you want to conform exactly to the draft C++ Standard, not all
_CATCH_IO_END C++ implementations support exception handling. I define the set of
_CATCH_:IO_ (x) macros _TRY_:IO_BEG:IN, _CATCH_:IO_END, and _CATCH_IO_ (x) to encapsu-
late the code changes needed to deal with diverse environments. Within a
member function of a class derived from ios, you can write:
_TRY_:IO_BEG:IN
II perfo~ streambuf operations
_CATCH_:IO_END
This expands to code that catches all exceptions thrown between the two
macros. The catch clause sets the status bit ios: : failbit and propagates
the exception. Within any other function that operates on an object x
derived from ios, you replace the second macro with _CATCH_:IO_(x).
_RERA:ISE The above macros are mostly based, in turn, on macros introduced for
the header <exception>. (See pages 72 and 77.) The only new creature is
the macro _RERAl:SE. The internal header <yxvals. h> defines this macro
as the expression throw, to propagate a pending exception, if exceptions
are implemented, or as xmsg: : _Throw (0) if not. (See page 72.)
enumeration Still another implementation artifact is the handling of enumeration
overloading overloading. As I mentioned on page 121, the Committee has added
enumeration overloading to C++, but not all implementations have caught
up with this change. That poses several problems in the writing of this and
later standard headers. The draft C++ Standard specifies that several
bitmask types be defined as enumerations with bitwise operators over-
loaded. But older implementations balk at such antics. So an implementa-
tion provides these overloaded operator definitions for a bitmask type only
<ioe> 129
Figure 6.1: II ios standard header
#ifndef _J:OS_
ioe #define _J:OS_
Part 1 of 3 #include <exception>
II J:/O exception macros
#if _CATCH_IO_EXCEPTJ:ONS
#define _TRY_J:O_BEGIN _TRY_BEGJ:N
#define _CATCH_J:O_END _CATCH_ALL \
setstate(badbit); _RERAISE; _CATCH_END
#define _CATCH_J:O_(x) _CATCH_ALL \
(x).setstate(ios::badbit); _RERAJ:SE; _CATCH_END
telae
#define _TRY_J:O_BEGJ:N {
#define _CATCH_J:O_END }
#define _CATCH_J:O_(x) }
#endif
class ostream; class streambuf;
II class ios
class ios (
public:
II class failure
class failure : public xmsg
public:
failure(const char *_X 0, const char *_Y 0,
const char *_Z = 0)
: xmsg(_X, _Y, _Z) {};
virtual -failure();
protected:
virtual void do_raise();
};
enum _Fmtflags {skipws = Ox0001, unitbuf = Ox0002,
uppercase = Ox0004, showbase = oxoooe,
showpoint = Ox0010, showpos =Ox0020,
left = Ox0040, right = oxooeo, internal OxOl00,
dec = Ox0200, oct = Ox0400, hex = oxoeoo,
scientific = Oxl000, fixed = Ox2000,
adjustfield = OxOlcO, basefield = OxOeOO,
floatfield = Ox3000, _Fmtmask = Ox3fff, _~tzero O};
enum _Iostate (goodbit = OxO, eofbit = Oxl,
failbit = Ox2, badbit = Ox4, _Statmask = Ox7);
enum _Openmode {in = OxOl, out = Ox02, ate = Ox04,
app = oxoe, trunc = Oxl0, binary = Ox20};
enum seekdir {beg = 0, cur = 1, end = 2};
_BITMASK(_Fmtflags, fmtflags);
_BJ:TMASK(_J:ostate, iostate);
_BJ:TMASK(_Openmode, openmode);
typedef short io_state, open_mode, seek_dir;
enum _Uninitialized {_Noinit};
II class J:nit
class Init (
public:
J:nit();
-Init();
private:
static int _J:nit_cnt;
130 Chapter 6
Continuing );
II class _Iosarray
ios
class _Iosarray (
Part 2 of 3 public:
_Iosarray(int _Idx, _Iosarray *_Link = 0)
: _Hext(_Link), _Index(_Idx), _Lo(O), _VP(O) ()
_Iosarray *_Next;
int _Index;
long _Lo;
void *_Vp;
);
ios(streambuf *_S)
(init(_S); )
ios(const ios& _R)
(init(O), *this = _R; )
ios& operator=(const ios&);
virtual -ios();
operator void *() const
{return (void *)(I*this ? 0 : this);
_Boo1 operatorl() const
{return «_State & (failbitlbadbit» 1= 0); )
ios& copyfmt(const ios&);
ostream *tie() const
(return (_Tiestr); )
ostream *tie(ostream *);
streambuf *rdbuf() const
(return (_Sb); )
streambuf *rdbuf(streambuf *);
iostate rdstate() const
{return (_State); }
void c1ear(iostate = goodbit);
void c1ear(io_state _St)
(c1ear«iostate)_St);
void setstate(iostate _St)
(c1ear(_State I _St); )
void setstate(io_state _St)
(setstate«iostate)_St);
_Boo1 goode) const
{return (_State == goodbit);
_Boo1 eof() const
{return (_State & eofbit); )
_Bool fail() const
{return (_State & (badbit I failbit»;
_Bool bade) const
{return (_State & badbit);
iostate exceptions() const
(return (_Except); )
void exceptions(iostate);
void exceptioDs(io_state _St)
(exceptions«iostate)_St);
fmtflags flags() const
{return (_~tf1); }
fmtflags flags(fmtflags);
fmtflags setf(fmtflags);
fmtf1ags setf(fmtflags, fmtflags);
<ios> 131
Continuing void unsetf(fmtflags);
int fill() const
ios (return (_Fillch);
Part 3 of 3 int fill(int);
int precision() const
(return (_Prec);
int precision(int);
int width() const
(return (_Wide);
int width(int);
static int xalloc()
(return (_Index++);
long& iword(int _Idx)
(return (_Findarr(_Idx) __Lo);
void *& pword(int _Idx)
(return (_Findarr<_Idx) __VP);
protected:
ios()
(init(O); }
ios(_Uninitialized)
{}
void init(streambuf *);
private:
streambuf *_Sb;
ostream *_Tiestr;
iostate _State, _Except;
fmtflags _Fmtfl;
int _Prec, _Wide;
char _Fillch;
static int _Index;
_Iosarray *_Arr;
_Iosarray& _Findarr(int);
void _Tidy();
};
II manipulators
ios& dec(ios&);
ios& fixed(ios&);
ios& hex(ios&);
ios& internal(ios&);
ios& left(ios&);
ios& noshowbase(ios&);
ios& noshowpoint(ios&);
ios& noshowpos(ios&);
ios& noskipws(ios&);
ios& nouppercase(ios&);
ios& oct(ios&);
ios& right(ios&);
ios& scientific(ios&);
ios& showbase(ios&);
ios& showpoint(ios&);
ios& showpos(ios&);
ios& skipws(ios&);
ios& uppercase(ios&);
#endif D
132 Chapter 6
if it supports enumeration overloading. Otherwise, an implementation
defines each of the bitmask types as a synonym for into
But the draft C++ Standard also specifies several "optional" types for
backward compatibility. These are all traditionally synonyms for type into
A number of member functions are overloaded on the newer bitmask type
and the older type. To avoid ambiguous function signatures, this imple-
mentation defines the optional types as synonyms for type short.
_Fmtflags Perhaps now you can see why the bitmask types are defined in two
:Iostate stages. The first stage declares an enumeration with a secret name -
_Opemnode _Fmtflags,_:Iostate, or _Opemnode. The second stage ties the secret name
to the public name - fmtflags, iostate, or opemnode, respectively. It also
supplies the overloaded function definitions, if the implementation sup-
ports enumeration overloading. Otherwise, the public name is defined as
a synonym for into
_B:ITMASK The macro _B:ITMASK encapsulates this tedious process. The internal
header <yxvals •h> contains either the macro definition:
#define _B:ITMASK(E, T) \
E& qperator&=(E& _x, E _Y) \
(_X = (E) (_X & _Y); return (_X); } \
E& qperatorl=(E& _X, E _Y) \
{_X = (E) (_X I _Y); return (_X); } \
E& operatorA=(E& _X, E _Y) \
(_X = (E) (_X A _Y); return (_X); } \
E& qperator&(E _X, E _Y) \
(return «E)(_X & _Y»; } \
E& qperatorl(E _X, E _Y) \
(return «E)(_X I _Y»; } \
E& qperatorA(E _X, E _Y) \
(return «E)(_X A _Y»; } \
E& qperatorN(E _X) \
(return «E)N_X); ) \
typedef E T
if enumerations are overloadable. or:
#define _B:ITMASK(E, T) typedef int T
(Yes, much of the first definition can be replaced by a template, but not all
of it. And templates aren't available in some current implementations.)
_uninitialized Another piece of implementation-specific magic is the secret enumera-
tion _uninitialized. I use it as an explicit warning that something un-
usual is happening. The draft C++ Standard requires that the standard
streams be usable even within static constructors. No reliable way currently
exists for ordering initialization among static constructors in separate
translation units. Hence, the objects that implement the standard streams
cannot rely on the normal workings of static constructors to get the streams
ready in time.
double The solution is to double construct certain critical objects in the iostreams
construction portion of the Standard C++ library: (While the theology of this practice is
still questionable, every implementation of C++ that I know of makes it
work right, of necessity:) To ensure that the actual object initialization
occurs only when expected, the static constructors for these critical objects
<ios> 133
must assuredly do nothing. (So too must certain destructors, but that's
another part of the star)!.) I discuss these matters at greater length in
Chapter 14: <iostream>.
_Noinit Thus, certain iostream constructors have a single parameter of type
_Uninitialized. (See, for example, Chapter 7: <streambuf>, Chapter 8:
<istream>, Chapter 9: <ostream>, and Chapter 13: <fstream>.) The only
legitimate value of this type is _Noinit. A constructor such as stream-
buf (ios : : _Noini t) clearly advertizes that it performs no initialization of
its object members.
_iosarray One more implementation artifact is worthy of remark. The member
functions iword and pword demand some internal representation for ex-
tensible arrays. I chose to represent both, for simplicit)', in one singly linked
list. Class _iosarray describes a list element. An object of this class stores:
• a value of type long
• a value of type pointer to void
• the index value for the element
• a link to the next element in the list
The list is not kept in any particular order.
_Findarr The private member function _Findarr maintains a linked list of these
elements for each ios object. As you can see, both iword and pword are
implemented in terms of a single call to _Findarr.
ioe. c Figure 6.2 shows the file ios. c. It defines all the member functions likely
to be required by any object of class ios. That includes overrides for virtual
member functions in the exception class ios: : failure.
clear Three of the member functions are workhorses. clear (iostate) is the
init one place that checks whether to throw an ios: : failure exception. You
_Tidy will find that all other functions with this responsibility pass it off to this
member function. init (streambuf *) likewise performs all initializations
of ios objects. And _Tidy performs all cleanups. Its primary responsibility
is to free any list elements allocated by _Findarr.
destructor The destructor for class ios is bizarre. For an arbitrary ios object, it is
obliged to call _Tidy. But for the objects that control the four standard
streams, it is obliged not to do so. Just as these four objects must survive
double construction, so too must they endure double destruction. Other-
wise, you could not safely use the standard streams from static destructors,
which the draft C++ Standard explicitly allows. I chose this overt kludge,
over more subtle methods, to handle this unusual requirement.
iosassig.c Figure 6.3 shows the file iosassig.c, which defines the assignment
operator for class ios. The default versions of this function and the copy
constructor are unacceptable because of the need to copy an allocated list.
It is also conceivable that the act of copying will trip across the need to throw
an exception. Neither member function is large, but both have their fragile
aspects.
134 Chapter 6
Figure 6.2: II los -- ios basic members
#inc1ude <iostream>
ios.c
int ios::_Index = 0;
ios::failure::-fai1ure()
( II destruct a failure
)
void ios::failure::do_raise()
( II throw an ios::failure
_RAISE(*this);
)
ios::-ios()
{ II destruct an ios -- DO (ALMOST) NOTHING
if (this 1= (ios *)&cin && this 1= (ios *)&cout
&& this 1= (ios *)&cerr && this 1= (ios *)&c10g)
_Tidy ( );
void ios::_Tidy()
{ II discard storage for an ios
_Iosarray *ql, *q2;
for (ql =_Arr; ql 1= 0; ql q2)
q2 = ql->_Next, delete qi;
_Arr = 0;
o
<ios> 135
Figure 6.3: II iosassign -- ios::operator=(const ios&)
#inc1ude <ios>
iosassig.c
ios& ios::operator=(const ios& rhs)
{ II assign to an ios
if (this 1= &rhs)
( II safe to copy
_Sb = rhs._Sb;
_State = rhse_State;
copyfmt(rhs); II cause any throw at end
)
return (*this);
o
ioscopyf. c Figure 6.4 shows the file ioscopyf • c, which defines the member func-
iosexcep. c tion copyfmt (const ios&:) . Note that it, too, is careful to complete its work
before risking an operation that can throw an exception. Figure 6.5 shows
the file ioexcep.c, which defines the member function excep-
136 Chapter 6
Figure 6.6: II iosfi11 -- ios::fi11(int)
#inc1ude <ios>
iosfill.c
int ios::fi11(int nf)
{ II set fill character
char of = _Fi11ch;
_Fi11ch = nf;
return (of);
o
mask. The function replaces format flags selected by the second operand
with corresponding flags from the first operand.
iostie. c • Figure 6.12 shows the file iostie •c, which defines the member function
tie(ostream *).
ioswidth.c - Figure 6.13 shows the file ioswidth.c, which defines the member
function width (int) .
iosunset. c Figure 6.14 shows the file iosunset. c, which defines the member
function unsetf (fmtflags). Unlike its fellow flag-twiddling functions, it
makes no attempt to return the previous stored value.
iosarray.c Figure 6.15 shows the file iosarray.c, which defines the function
_Findarr. It locates an existing list element with the desired index, if
possible. Otherwise, it recycles a list element or allocates a new one.
The remaining source files implement manipulators for class ios:
dec. c • Figure 6.16 shows the file dec. c, which defines the function dec (ios&:) .
fixed.c - Figure 6.17 shows the file fixed.c, which defines the function
fixed ( ios&: ) .
hex. c • Figure 6.18 shows the file hex. c, which defines the function hex (ios&:).
<ios> 139
internal.c - Figure 6.19 shows the file interna1.c, which defines the function
internal (ios&) .
left. c - Figure 6.20 shows the file left. c, which defines the function
left (ios&).
noskipws. c - Figure 6.21 shows the file Doskipws. c, which defines the function
noskipws (ios&).
noupperc. c - Figure 6.22 shows the file Doupperc. c, which defines the function
nouppercase(ios&).
nshowbas. c - Figure 6.23 shows the file Dshowbas. c, which defines the function
noshowbase (ios&).
Dshowpoi. c - Figure 6.24 shows the file Dshowpoi. c, which defines the function
noshowpoint (ios&) .
Dshowpos. c - Figure 6.25 shows the file nshowpos. c, which defines the function
noshowpos ( ios& ) .
oct.c - Figure 6.26 shows the file oct. c, which defines the function oct (ios&).
right.c - Figure 6.27 shows the file right.c, which defines the function
right (ios&:) .
scientif. c - Figure 6.28 shows the file scientif. c, which defines the function
scientific(ios&).
showbase.c - Figure 6.29 shows the file showbase.c, which defines the function
showbase (ios&).
showpoin.c - Figure 6.30 shows the file showpoin.c, which defines the function
showpoint (ios&) .
showpos. c - Figure 6.31 shows the file showpos. c, which defines the function show-
pos(ios&).
skipws. c - Figure 6.32 shows the file skipws. c, which defines the function
skipws (ios&).
uppercas.c - Figure 6.33 shows the file uppercas.c, which defines the function
uppercase ( ios& ) .
:tnit I defer discussion of the code for class ios: : :tnit until much later, for
reasons that will become apparent. (See Chapter 14: <iostream>.)
Testing <ios>
tios. c Figure 6.34 shows the file tios. c. It exercises the numerous member
functions of class ios. To inspect a couple of nontrivial ios objects, the
program obtains pointers to the base subobjects of cin and couto Some of
the tests that follow depend on the required initial values stored in these
objects. (See Chapter 14: <iostrea.m>.)
tics. c is hardly exhaustive, but it tries to be a bit finicky about details.
I found it handy in testing the code written specially for this implementa-
tion. If you are testing an existing implementation for conformance to the
draft C++ Standard, you might also appreciate this level of attention. Small
variations in class ios among existing implementations lead to nuisancy
problems in porting code, and in bringing the code into conformance.
Exercises
Exercise 6. 1 Write a manipulator ioreset that restores all formatting information to its
initial values.
Exercise 6.2 Write a manipulator myset that restores all formatting information to a set
of values that you specify: Describe at least three sensible ways to specify
such a set of values.
Exercise 6.3 What is the purpose of having the member function signature fill (int)
instead of fill (char)?
Exercise 6.4 This implementation of ios : : _Findarr (int) recycles allocated elements
that contain only zeros. (See page 138.) Why do you think it does so? Write
a simpler version of the member function that avoids this extra work. What
caveats would you give programmers concerning the use of this simplified
version?
144 Chapter 6
Figure 6.34: II test <ios>
#include <assert.h>
tios.c Part
#include <limits.h>
1 of 2 #include <ios>
#include <iostream>
II static data
static ios::fmtflags ffl[] = (
ios::dec, ios::fixed, ios::hex, ios::internal, ios::left,
ios::oct, ios::right, ios::scientific, ios::showbase,
ios::showpoint, ios::showpos, ios::skipws, ios::unitbuf,
ios::uppercase, ios::adjustfield, ios::basefield,
ios::floatfield);
static ios::iostate ifl[] = {
ios::badbit, ios::eofbit, ios::failbit, ios::goodbit};
static ios::openmode ofl[] = (
ios::app, ios::ate, ios::binary, ios::in, ios::out,
ios::trunc);
static ios::seekdir sfl[] = (
ios::beg, ios::cur, ios::end);
int maine}
( II test basic workings of ios definitions
ios *pi = &:cin;
ios *po = &:cout;
ios x{pi->rdbuf{»;
II test fmtflags groups
assert{(ios::left I ios::right I ios::internal)
== ios::adjustfield);
assert{(ios::dec I ios::oct I ios:: hex} == ios::basefield};
assert{(ios::scientific I ios::fixed} == ios::floatfield);
II test assignment and control functions
assert{(void *}x 1= 0 &:&: Ix == 0);
assert (x.tie{&:cerr) == 0 &:&: x.tie{) == &:cerr};
assert{x.rdbuf{po->rdbuf{}} == pi->rdbuf{)
&:&: x.rdbuf{) == po->rdbuf()};
x.clear{), assert(x.good() &&: x.rdstate(} == ios::goodbit);
x.clear{ios::badbit};
assert {x.bad(} &:&: x.rdstate() == ios::badbit};
x.setstate(ios::failbit);
assert{x.fail() &:& Ix.eof{)}, x.setstate(ios::eofbit};
assert (x.rdstate{)
== (ios::badbit I ios::eofbit I ios::failbit});
x.clear{ios::eofbit), pi->clear{ios::failbit);
assert(x.copyfmt{*pi).rdstate{) == ios::eofbit);
x.exceptions{ios::badbit);
assert (x.exceptions{) == ios::badbit);
II test format control functions
assert(x.flags{ios::oct) == {ios::skipws ios::dec}
&:&: x.flags(} == ios::oct);
assert(x.setf{ios::showbase} == ios::oct);
assert{x.setf{ios::scientific}
== (ios::oct I ios::showbase»;
<ios> 145
Continuing assert(x.setf«ios::fmtflags)-O, ios::unitbuf)
tios.c Part == (ios::oct I ios::scientific I ios::showbase»;
x.unsetf(ios::oct I ios::showbase);
20f2 assert(x.flags() == (ios::scientific I ios::unitbuf»;
assert (x.precision(INT_MIN) == 6
&& x.precision() == INT_MIN);
assert (x.width(INT_MAX) == 0 && x.width() == INT_MAX);
assert(x.fill('y') == ' , && x.fill() == 'y');
II test additional storage
int i = pi->xalloc();
int j = pi->xalloc();
assert(i == j - 1 && pi->iword(i) == 0 && pi->pword(j) 0);
pi->iword(i) = 3, pi->pword(j) = (void *)&cerr;
x.copyfmt(*pi).iword(j) = 4;
assert (pi->iword(i) == 3 && pi->pword(i) == 0);
assert(pi->iword(j) == 0 && pi->pword(j) == (void *)&cerr);
assert (x.iword(i) == 3 && x.pword(i) 0);
assert (x.iword(j) == 4 && x.pword(j) == (void *)&cerr);
II test manipulators
pi->unsetf«ios::fmtflags)-O);
dec(fixed(internal(showbase(showpoint(*pi»»);
assert (pi->flags() == (ios::dec I ios::fixed I ios::internal
I ios::showbase I ios:: showpoint»;
po->unsetf«ios::fmtflags)-O);
hex(left(scientific(showpos(skipws(*po»»);
assert (po->flags() == (ios::hex I ios::left
I ios::scientific I ios::showpos I ios::skipws»;
noshowbase(noshowpoint(noshowpos(noskipws(*po»»;
uppercase(oct(right(*po»);
assert(po->flags() == (ios::oct I ios::right
I ios:: scientific I ios::uppercase»;
scientific(nouppercase(*po»;
assert (po->flags() == (ios::oct I ios::right
I ios::scientific»;
cout « "SUCCESS testing <iOS>" « endl;
return (0);
D
Exercise 6.5 This implementation of ios: : _Findarr (int) , on the other hand, contains
few additional optimizations besides the one discussed in the previous
exercise. Show how you would alter it if:
• both ios: : iword (int) and ios: :pword( int) are used heavily, or
• list elements are numerous, seldom altered, but often accessed
Exercise 6.6 A wide-character stream traffics in elements of type we ha r_t instead of type
char (a single-byte stream). What would you have to change in class ios to
make it suitable for use with a wide stream? Can you change the class in
such a way that it is suitable for use with both wide-character and single-
byte streams?
Exercise 6.7 [Harder] Define the class ins that retains only the information required for
extractions, and the class outs that retains only the information required
146 Chapter 6
for insertiolls. Is there sufficient benefit to define both in terms of a common
base that retains overlapping information? Is there any reason not to do so?
Exercise 6.8 [Very hard] Devise a method for specifying the order of construction of
static objects across translation Ullits. You want to be able to specify that the
standard streams objects are constructed first and destroyed last. You also
want tIle progran1ffier to be able to use the same machinery for program-
specific purposes.
Chapter 7: <streambuf>
Background
The header <streambuf> has as its primary focus the class streambuf,
which serves as the driving engine for all iostreams operations. It also
defines the type streamoff and the class streampos, both of which aid in
altering the next character position to insert to or extract from in a sequence
controlled by an object of class streambuf.
streamoff The type streamoff is a (conceptual) borrowing from the Standard C
library: It is used to represent signed offsets for positioning operations
within a stream controlled by a streambuf object. That, of course, includes
C streams controlled by objects of type FJ:LE. A streamoff object must thus
be able to represent the offsets used by the functions fseek and ftell, both
declared in < stdio. h>. Hence, the type is pretty much constrained to have
the same representation as type long. Sadly, type streamoff has the same
double meaning as its Standard C counterpart:
• It can be a signed offset relative to some position within a stream. All
values are meaningful.
• It can be an absolute position within some stream. Negative values are
nonsensical. (And a file, in particular, may be so large that it has positions
not representable as values of type streamoff.)
It is not always clear from context which meaning is intended.
EOF Yet another definition is borrowed from the Standard C library. Macro
EOF has exactly the same definition, and meaning, as in the header
<stdio.h>. It is an integer value distinguishable from any value repre-
sentable as type unsigned char. And, as in the Standard C libral)T, it is used
here to signal either end-of-file when reading or some other kind of in-
put/output failure. The Standard C I/O model also influences the io-
streams machinery in more subtle ways. In particular, the remainder of
header <streambuf> defines two classes:
• streampos, for describing arbitrary positions within a stream
• streambuf, for controlling input and output to a stream
Both strive to be abstract data types. The "streams" they control can be files
in the Standard C sense, or text strings that grow and shrink dynamically
in memol)T, or an open-ended set of user-defined sources and sinks.
148 Chapter 7
Nevertheless, an important use for both these classes is to interface with
their Standard C counterparts. Hence, class streampos is shaped internally
by the needs of the Standard C functions fgetpos and fsetpos, declared
in <stdio.h>. And class streambuf is obliged to work well with the
streams of Standard C. Like it or not (and many C++ purists definitely do
not like it), you will find artifacts of Standard C in both these classes.
streampos As you might guess by no\\', class streampos is an attempt to improve
upon the type fpos_t, defined in <stdio .h>. In many ways it succeeds:
• You can add a streamoff value to a streampos object, or subtract a
streamoff value from a streampos object, to determine a new stream
position.
• You can subtract a streampos object from another streampos object to
obtain a streamoff difference.
• You can compare two streampos objects for equality or inequality.
fpos_t By comparison, an fpos_t value is just a magic cookie. All you can do is
obtain a current file position by calling fgetpos, copy the value about, and
use that value to restore the same file position by calling fsetpos (for the
same open stream, of course).
For an in-memory stream, a streamoff value stored inside a streampos
object probably suffices to represent all sensible stream positions. The same
is true of most disk files in a UNIX environment. All this arithmetic makes
sense and is easy to perform. But for a C stream running under an arbitrary
operating system, you have to rely in the end on the Standard C file-posi-
tioning functions. If they can't do what you ask, all this flexibility is for
naught. Operating-system support for file positioning is sufficiently varied
that you dare not be too ambitious, at least not in a program you hope to
keep portable.
streambuf I save many of the details of class streambuf for later chapters that
describe classes derived from this class. For now, I focus on what you need
to know to understand how this class functions as a base class for more
interesting offspring. That's a hard enough tale to tell.
Conceptuall)T, every streambuf object controls an input stream and an
output stream. Often, one of the two streams doesn't really exist. All
operations on the nonexistent stream simply fail. If there are indeed two
streams, there need be no connection between them, but usually there is.
Each stream can maintain a separate position indicator, but they can also
be tied together and move in concert.
inserters Class streambuf describes a very general engine for managing input
extractors and output character sequences. The class offers a public interface for
civilian use. Typicall)T, the inserters associated with an ostream object, such
as cout, calion member functions in this public interface to "insert charac-
ters into an output sequence." And the extractors associated with an
istream object, such as cin, calion other member functions in this public
interface to "extract characters from an input sequence."
<streambuf> 149
buffer An object of class streambuf stores six pointers. These point at the
pointers beginning, current character, and just past the end of the input and output
buffers. The member function that inserts a character need make only a
single pointer comparison to determine whether space exists in the buffer
(and perhaps a check that the buffer exists). Andthe member functions that
extract (or peek at) a character need make only a similar comparison to
determine whether a character exists in the buffer. An implementation
typically provides inline definitions of all these member functions, so
inserters and extractors can move characters as efficiently as possible.
But what happens when an output buffer is full (or nonexistent)? Or
what if an input buffer is empty (or nonexistent)? Then, and only then, do
the public member functions call in tum on protected virtual member
functions. Here is where the flexibility comes in. A class derived from
streambuf can override these virtuals all sorts of ways:
• For true buffered file 1/0, they can endeavor to read and write hundreds
or thousands of characters at a time using allocated buffers.
• For unbuffered file I/O, they can read or write a single character at a
time, never setting up a buffer.
• For an in-memory stream buffer, they can allocate and free storage as
needed to hold a character sequence that varies in length.
The possibilities are, in fact, endless. I've merely outlined the kinds of
stream buffers provided as part of the Standard C++ library: Once you see
a few variations on the streambuf virtual member functions, you can use
your own imagination to contrive more.
Footnotes:
Footnotes 86) This macro is also defined, with the same value and meaning, in
<stdio.h>.
87) The conversion state is used for sequences that translate between wide-char-
acter and generalized multibyte encoding, as described in Amendment 1 to
the C Standard.
88) The next character to read or write is the first character in the sequence.
89) The default constructor is protected for class streambuf to assure that only
objects for classes derived from this class may be constructed.
<streambuf> 159
90) A class derived from streambuf can override the virtual member function
underflow () with a function that returns a value other than EOF without
making a read position available. In that event, streambuf: :uflow()
must also be overridden since the default behavior is inadequate.
91) Classes derived from streambuf can provide more efficient ways to
implementxsgetn and xsputn by overriding these definitions in the base
class.
Future Directions
Debate continues on whether the descriptions of the streambuf pro-
tected virtual member functions shown here are adequate. I know of no
intention to change the functionality described in any fundamental way: Any
rewrite, however, is bound to alter the interpretation of some boundary
cases. I wouldn't write code that depends on too legalistic an interpretation
of extreme conditions in such a notoriously delicate area of the draft c++
Standard.
sho'WIUany Yet another streambuf virtual member function has already been
added. The public access function for it is called showmany (pronounced
"ess-how-many/' not "show-many"). You call this function to determine
how many characters you can safely extract with no fear of blocking while
waiting for additional input. When an implementation can't give an honest
answer, it is permitted to give an ultraconservative answer instead.
streampos There is still ongoing discussion of ways to make the class streampos
more open-ended. Right now, an object of this class is obliged to store
sufficient information to position an in-memory stream or a C stream. A
class derived from streambuf, however, may have additional needs. It is
possible that the definition of streampos may be altered to allow it to be
extended.
wide-character As I mentioned in the previous chapter, on page 119, a major change
streams already made adds wide-character streams. Class streambuf derives from
a template class parameterized by the type of the stream element. The
instantiation for type char has essentially the same functionality as de-
scribed in this chapter. Another, for type wchar_t, SllpportS streams of
elements from some large character set. Class streampos likewise derives
from a template class.
Using <streambuf>
You have little or no occasion to include the header <streambuf> di-
rectI}'. Like <ios> in the previOtlS chapter, it is another header invented by
the Committee simply to split the iostreams declarations into more man-
ageable pieces. But the chances are equally good that you'll use the contents
of <streambuf> in a program. Class streambuf is the base class for all
stream buffers, which control all input and output in the iostreams package.
The base class su.mmarizes what is common to all stream buffers, or the
default behavior for an unused feature in a particular stream buffer.
160 Chapter 7
public You may never have occasion to use the streambuf interface yourself.
interface Even if you write your own inserters and extractors, you often do the
low-level work by calling on existing inserters and extractors. In fact, I'd
discourage you from making direct calls on a streambuf object, at least
until you've had time to study some existing inserters and extractors and
really understand the protocol. Any abstract description you read will
never compare to first-hand practical experience. For now, I'll characterize
the public interface to class streambuf in the briefest of terms.
When extracting from a sequence:
sbwnpe • Peek at the next character by calling sgete ( ), extract (and point past) it
sgete by calling sbumpe ( ), or put back the last character eh you extracted by
sputbaeke calling sputbaekc (eh). (Do not put back a different character, even
though the draft c++ Standard permits you to do so.)
sgetn - Read up to ncharactersinto a bufferbuf by calling sgetn(buf, n). The
return value tells you how many characters you actually read.
snextc - For various reasons of performance or reliabilit)T, avoid calling snexte ( )
sungete or sungete () . Also avoid calling sputbaeke (eh) except as I described
above.
When inserting into a sequence:
spute • Write a character ch by calling spute (eh).
sputn - Write up to n characters from a buffer buf by calling sputn(buf, n).
The return value tells you how many characters you actually wrote,
which should always be n.
pubseekoff One way to position within a sequence is to use the function pubseek-
off. The next position to access (extract from or insert to) is determined by
an offset of type streamoff and a value of the enumerated type ios: :
seekdir. The latter argument can take the values:
beg - ios:: beg, to specify an absolute offset relative to the beginning of the
sequence
cur - ios:: cur, to specify an algebraic offset relative to the current position
within the sequence
end - ios:: end, to specify an algebraic offset relative to the end of the se-
quence
This is the same set of positioning capabilities as provided by the Standard
C library function fseek, declared in <stdio.h>. When reading and writ-
ing a C stream, both sets suffer the same potential system-dependent
limitations. (See Chapter 13: <fstream>.)
openmode An argument of type ios: :openmode (yet another misnomer) has
in ios:: in set to affect the input sequence, and ios: :out set to affect the
out output sequence. The effect of various combinations of arguments depends
very strongly on the actual type derived from the base streambuf. A C
stream, for example, maintains only one file position indicato~ so this
argument is effectively ignored. For other types of stream buffers, you may
be able to position input and output streams separately or in tandem.
<streambuf > 161
pubseekpos Note, by the wa)T, that pubseekoff returns an object of class seekpos.
You need such an object to represent an arbitrary position within an
arbitrary stream. A reliable way to memorize the current stream position,
in fact, is to write:
streampos pos = x.pubseekoff(O, ios::cur)
for the streambuf object x. You can later return to the memorized position
by writing:
x.pubseekpos(pos);
Sometimes you know that a simple stream offset will do the job. An object
of class strstreambuf, for example, deals with a sequence of characters in
memory: (See Chapter 11: <strstream>.) In such a case, you can safely
traffic in stream offsets. You can memorize a position by writing:
streamoff off = x.pubseekoff(O, ios::cur).offset();
and later restore it with:
x.pubseekoff(off, ios::beg);
I encourage you to indulge in as little stream positioning as possible,
however. When you must, deal with objects of class streampos as much as
possible. Don't count on arithmetic working properly on such objects,
despite the implied promises of the draft C++ Standard. And try to confine
yourself to revisiting positions that you've visited earlier, using the idiom
I just showed. If you're more ambitious, expect occasional surprises, and
positioning failures.
pubsetbuf Class streambuf has two other public member functions. You can go a
long time without having occasion to call either one. pubsetbuf is a general
hook for conveying buffering information to a stream buffer. It calls in turn
the protected virtual member setbuf. The only defined use for this public
member function in the draft C++ Standard is for class filebuf. (See
Chapter 13: <fstream>.) There, it effectively lets you call the function
setvbuf, declared in <stdio.h>, on behalf of the buffer controlled by
filebuf. (Even that operation promises little or nothing in the way of
actual control over file buffering.)
pubsync The remaining public member function for class streambuf is pubsync.
It provides yet another hook, this time to the protected virtual member
function sync. The public member function is used to synchronize a stream
buffer with any separate representation of the stream it controls. For an
object of class filebuf, the effect is the same as calling fflush, declared in
<stdio • h>, for a C stream. Other types of stream buffers may have little or
nothing to do to ensure synchronization. In any event, such synchroniza-
tion calls often occur in the course of other iostreams operations. You may
have little or no occasion to add explicit calls yourself. When you do, you
will find higher-level functions that call pubsync with a bit more checking.
(See, for example, the function ostream: : flush, on page 221.)
protected So much for the public interface. Class streambuf also offers a protected
interface interface which is even more sophisticated. You care about this interface,
162 Chapter 7
naturally enough, only if you derive your own class from a st.'r."Aambuf base.
(That's the only way you can even access this interface.)
The first part of the protected interface you can, and sho1.tld, pretty much
ignore. It is used by the public members to conduct their bllsiness. I-Iere
again you will find a number of funny names and irregularities. But that
should be of no concern to the typical user of a class derived from stream-
buf. Look at the public member functions, nearly all of which are typically
defined as inline, to get a feel for what they do, if you care.
protected You might note, in passing, that the constructors for class streambuf are
constructors protected. That keeps you from constructing an object of this class outside
captivit}T. The idea is to derive from the base class and specialize it to
perform useful operations.
The second part of the protected interface is where the action is. Class
streambuf is built around a set of virtual member functions that allow you
to tailor behavior over a very wide range. Here is where insertions get
turned into actual writes to some physical sink for characters. Here is where
extractors get turned into actual reads from some physical source of char-
acters. Here is where positioning operations get real, or those other hooks
I mentioned earlier get something hung upon them. In short, the art of
specializing streambuf is the art of writing virtual member functions that
honor the protocol of the base class.
Until the draft C++ Standard came along, there was only one way to
specialize a streambuf. Find some code for another specialization that
more or less worked, and that you could more or less understand, and crib
like crazy: Having access to the source for streambuf proper also helped
immeasurably.
Now you have a little more help. You can read the draft C++ Standard
to learn the protocol that must be honored by these virtual member func-
tions. Then you can go look at some specialzation that works and that you
can more or less understand, and crib like crazy: I can't honestly recom-
mend any other way to proceed.
Why is this so? Because describing the protocol for the streambuf virtual
member functions is the hardest piece of standardese I've ever essayed.
Jerry Schwarz, the original author of iostreams, is still not happy with its
current description. I think it's getting reasonably accurate and precise, but
I'd never accuse this part of the draft of being a decent tutorial. Learning
by imitation - and by doing - is still the safest route.
Having said that, I'll give you just the briefest of hints about what the
four most critical virtual member functions do:
overflow. overflow(ch) either writes the character ch to the physical sink of
characters, somehow, or it "makes a write position available" (creates an
in-memory buffer with a space available for writing a character) and
puts the character in it.
<streambuf> 163
pbackfail - either writes the character ch back to the physical source
pbackfail (ch)
of characters, somehow, or it "makes a putback position available"
(creates an in-memory buffer with a space available for putting back a
character) and puts the character in it.
underflow. underflow ( ) either reads a character from the physical source of char-
acters without consuming it, somehow, or it "makes a read position
available" (creates an in-memory buffer with a character available for
reading) and reads the character from it without consuming it.
uflow - uflow () either reads a character from the physical source of characters
and consumes it, somehow, or it "makes a read position available"
(creates an in-memory buffer with a character available for reading) and
reads and consumes a character from it.
The Committee added uflow for reasons I indicated earlier, on page 149.
The public member functions call this new protected virtual member
function only to read a character and point past it. To peek at a character,
without pointing past it, they call the older underflow instead. With this
discipline, the streambuf protected virtual member functions can avoid
buffering input characters when that proves inconvenient.
If your goal is to derive a stream buffer that works properly under these
specialized circumstances, make sure you understand the reasons for this
protocol and how to honor it. But in general, you can be quite comfortable
starting with one of the derived stream buffers presented later in this book,
as I indicated earlier.
Implementing <streambuf>
streambu Figure 7.1 shows the file streambu, which implements the standard
_SADOFF header <streambuf>. I have found it convenient in this implementation to
define the secret constant _BADOFF. As in Standard C, it is the standard way
to indicate an invalid absolute position, of type streamoff. Unfortunately,
it sometimes also is used to indicate an invalid relative position, as I
mentioned earlier in this chapter, on page 147. For this usage, the value-1
is a less compelling choice.
streampos The implementation I chose for class'streampos endeavors to succeed
at positioning requests as often as possible, even in the teeth of possible
failure. It stores within each streampos object two private member objects:
• _Pos, of type streamoff, to keep track of the simple offset arithmetic
described above
• _Fp, of type fpos_t, to keep track of a C file position when working with
actual files
The actual declared type of the latter is _Fpost because the name fpos_t
is not necessarily defined at this point. (The draft c++ Standard pays lip
service to the need for this component of class streampos, but is intention-
ally mealy mouthed about whether it is actually present.) The internal
header <yxvals. h> defines this type, as necessaI}', to match the accompa-
nying Standard C library definition.
fpos_t This implementation declares the structure fpos_t (_Fpost) with the
_!'post members:
• _Off, for the absolute position within a stream (_SADOFF if undefined)
• _Wstate, for the state memory when parsing a wide-character stream
The state memory, in turn, has the elements:
• _State, for the current parse and shift state within a multibyte stream
• _Wchar, for the current wide-character accumulator used by the mul-
tibyte stream parser
For all but wide-character streams, both these components are always zero.
pubsetbuf In class streambuf, the functions pubsetbuf and pubsync are really just
pubsync hooks into virtual functions that are specialized for derived classes. The
default behavior for the base class is to do nothing.
setp Finally, I've found it useful to add a three-argument version of setp, a
_Gn function _Gn that treats the "get next" pointer as a pointer to unsigned char,
_Pn and a similar function _Pn for the "put next" pointer.
spos. c Figure 7.2 shows the file spos. c, which defines the constructor for class
streampos. I added a default second argument to provide an optional
initial value for the fpos_t member object _!'p.
sposequa. c Figure 7.3 shows the file sposequa . c, which defines the equality opera-
tor for class streampos. Note that an invalid file position always compares
equal to another invalid file position, and never compares equal to a valid
one. The equality check for valid file positions may be too exacting.
<streambuf> 167
Figure 7.2: II spos -- streampos constructor
#include <streambuf>
spos.c
static const _Fpost fpzero = {O};
sposminu.c Figure 7.4 shows the file sposminu.c, which defines the subtraction
operator for class streampos. It yields _SADOFF if either file position is
undefined. Sadl~ there is no easy way to distinguish an invalid return from
the very sensible difference -1.
sposoffs.c Figure 7.5 shows the file sposoffs.c, which defines the member func-
tion off set ( ) for class streampos. Note that it ignores the algebraic offset
_Pos if the absolute position _Fp ._Off is invalid. (Yes, the names are
backwards.)
168 Chapter 7
Figure 7.6: II streambuf -- streambuf basic members
#include <string.h>
streambu.c #include <streambuf>
Part 1 of 2
streambuf::-streambuf{)
{ II destruct a streambuf -- DO NOTHING
}
int streambuf::overflowCint)
{ II d~ overflow, always fail
return (EOF);
int streambuf::pbackfail{int)
{ II d~ pbackfail, always fail
return (EOF);
int streambuf::underflow{)
{ II d~ underflow, always fail
return (EOF);
int streambuf::uflow{)
{ II consume and return a character
return {gptr{} 1= 0 && gptr{) < egptr{)
? *_Gn{)++
underflow{) == EOF ? EOF : *_Gn{)++);
int streambuf::sync()
( II dummy sync, always succeed
return (0);
void streambuf::_Init{)
( II initialize a streambuf with default pointers
_IGbeg = &_Gbeg;
_IGnext = &_Gnext;
_IGend = &_Gend;
_IPbeg = &_Pbeg;
_IPnext = &_Pnext;
_IPend = &:_Pend;
setp{O, 0);
setg{O, 0, 0);
) 0
170 Chapter 7
streambu.c Figure 7.6 shows the file streambu. c. It defines all the member functions
likely to be required by any object of class streambuf. I've augmented the
required constructor with an optional second argument, to ease interfacing
with the Standard C library: Most are dummy functions that either fail or
do something innocuous. The two flavors of the secret function _Ini t show
two ways to implement the six pointers that control all buffer accesses.
indirect I found it desirable to add a level of indirection. You will notice that the
pointers class indeed has six private pointer members. It also has six private pointers
to pointers to char. And it is these indirect pointers that the protected
member functions use to access characters in any in-memory buffers.
I chose this more ornate implementation to permit a very important
optimization. Remember, I said earlier that a principal use for streambuf
objects is to work in concert with C streams controlled by objects of type
FILE. The obvious way to do so is to derive a class whose virtual member
functions call fgetc, fputc, etc. for the corresponding FILE object. But that
is terribly inefficient. A better wa)T, when possible, is to have both the
streambuf and FILE objects control exactly the same set of pointers into
exactly the same buffers.
This I have been able to do in conjunction with my implementation of
the Standard C library: The cost is this extra level of indirection within class
streambuf. The payoff is substantially improved performance for c++
iostreams when reading and writing files. I consider it a good tradeoff.
So the six indirect pointers point directly inside a FILE object, whenever
possible, as I will show in a later chapter. (See Chapter 13: <fstream>.) In
this case, the six character pointers are ignored. Otherwise, the six indirect
pointers point at the six character pointers.
If you don't understand streambuf yet, don't worry: Its use will become
clearer as you see some of the ways it is specialized later in the Standard
C++ library.
Testing <streambuf>
tstreamb. c Figure 7.7 shows the file tstreamb. c. It tests the basic properties of class
streampos and class streambuf. For the latter, it relies on the specialization
provided by class strstreambuf, which is mediated in tum by class
ostrstream. (See Chapter 11: <strstream>.) Since class streambuf itself
does little, there is little to test.
Most of the serious testing of class streambuf occurs in connection with
the derived classes presented later in this book. If all goes well, the program
prints:
SUCCESS testing <streambuf>
and takes a normal exit.
<streambuf> 171
Figure 7.7: II test <streambuf>
#include <assert.h>
tstreamb.c #include <iostream>
#include <streambuf>
#include <strstream>
int maine)
{ II test basic workings of streambuf definitions
streamoff soff = 3;
streampos sl, s2(10);
assert(sl.offset() == 0 && s2.offset() 10);
assert(s2 - sl == 10);
assert(sl + 4 == s2 - 6);
assert(I«sl += soff) 1= (s2 -= 7»);
assert(sl.offset() == soff && s2.offset() soft);
II test streambuf positioning members
char buf[10];
ostrstream os;
streambuf *p = os.rdbuf();
assert(p 1= 0);
sl = p->pubseekoff(O, ios::cur, ios::out);
s2 = p->pubseekpos(sl, ios::out);
assert(sl == s2);
p->pubsetbuf(buf, sizeof (buf»;
p->pubsync();
II test streambuf read/write members
assert(p->sputc('a') == 'a');
sl = p->pubseekoff(O, ios::cur, ios::out);
assert(sl 1= s2);
s2 = p->pubseekpos(sl, ios::out);
assert(sl == s2);
assert (p->sgetc() == 'a' && p->sbumpc() 'a');
assert (p->sputn(lIxyz", 3) == 3);
assert (p->sgetn(buf, 10) == 3 && buf[O] 'x');
assert (p->sputbackc ('w') == 'w' && p->sungetc () 'y');
assert (p->snextc() == 'w');
assert (p->pubseekpos(sl, ios::in) == s2);
assert(p->sputc('I') == '1' && p->sgetc() == 'x');
assert«p->pubseekoff(O, ios::end, ios::in».offset() 1= -1);
assert (p->sungetc() == 'I');
cout « "SUCCESS testing <streambuf>" « endl;
return (0);
o
Exercises
Exercise 7. 1 Derive a class from streambuf that serves as a "bit bucket." Any characters
you insert in its output stream disappear without remark. Why would you
want such a capability?
Exercise 7.2 Derive a class from streambuf that serves as a "toupper filter." Any
characters you insert in its output stream are translated to uppercase, then
inserted in another stream buffer. How do you specify this destination?
172 Chapter 7
Exercise 7.3 Derive a class from streambuf that serves as a "toupper filter," as in the
previous exercise, except that it alters its input stream, not its output stream.
Why is this version more elaborate than the previous one?
Exercise 7.4 Define the class wstreambuf that behaves just like streambuf, but manages
streams of wide-characters instead of single-byte characters. What do you
have to change besides appearances of the type char?
Exercise 7.5 Define the template class basic_streambuf such that it can be specialized
to look like either streambuf or the wstreambuf of the previous exercise.
How many parameters do you need to specify?
Exercise 7.6 [Harder] Define a clase that serves as an "mbtowc filter." It extracts single-
byte characters from another stream buffer and converts them to a stream
of wide characters, as if by repeated calls to mbrtowc, declared in
<wchar.h> (15094). Is the reverse mapping easier or harder?
Exercise 7.7 [Very hard] Alter the class streambuf so that it manages both single-byte
and wide-character streams interchangeably. How can you "cross couple"
the streams to minimize surprises given a mixture of single-byte and
wide-character insertions or extractions?
•
Chapter 8: <1stream>
Background
The principal business of the header <istream> is to define the class
istream. This class is derived from the virtual public base class ios to help
you extract characters from a stream. The best known object of this class is
cin, which controls input from the standard input stream - the same C
stream controlled by stdin, declared in <stdio.h>. You can also create
i stream objects that control files you open by name, or strings of text stored
in memory, as later chapters reveal.
istream Most of the istream member functions fall into one of two broad
categories:
• unformatted input functions, which extract sequences of arbitrary char
values, such as uninterpreted text or binary data
• formatted input functions, which extract sequences that match certain text
patterns and convert them to encoded values of the various scalar and
string data types
unformatted The former group mostly overloads the names get and read. It is
input analogous to the Standard C library's fgetc and fread, declared in
<stdio.h>, but rather easier to use. For example, you can extract a "line"
of text with:
if (cin.getline(buf, sizeof (buf»)
<input succeeded>
The test is true (nonzero) only if the member function extracts a non-empty
string that can fit in buf, from the stream controlled by the istream object
cin. Aline is delimited (terminated) by an optional third argument of type
char, which defaults to ' \n' . The function stores the resulting null-termi-
nated string in buf, discarding the delimiter.
formatted The latter group of member functions overloads operator» to make
input the basic family of extractors. It is analogous to the Standard C library's
fscanf and friends, but with a variety of advantages. For example, you can
extract an octal integer with:
int n;
if (cin » oct » n)
<input succeeded>
174 Chapter 8
The test is true only if the second extractor extracts a non-empty field that
matches the pattern for octal integers, and the converted value can be
properly represented in an object of type into
Extractors are second in popularity only to inserters (formated output
functions) as a selling point for iostreams over more conventional Standard
C input and output. But they are easier to explain after you gain more
familiarity with the unformatted input functions. I return to extractors later
in this section.
Remember that all extractions and insertions in iostreams are mediated
by objects of class streambuf. (See the previous chapter.) An object of class
i stream finds its related streambuf object via a pointer in its base subobject
of class ios. A member function of class istream can extract and consume
the next available input character by calling rdbuf () ->sbumpc (). It can
extract and not consume the next available input character (peek at it) by
calling rdbuf () ->sgetc (). And it can put back a character ch by calling
rdhuf () ->sputbackc (ch). (It can perform several other related functions
as well, but these are the most robust.)
ipfx There is one small problem, however. It is perfectly permissible for the
isfx pointer to streambuf to be null. It is also quite possible that the pointer is
non-null, but the stream is in some error state that should discourage
extracting. Thus, it behooves any input function to look before it leaps. The
canonical way to play safe is to wrap every extractor member function with
two calls to istream member functions:
if (ipfx(noskip»
<perform any input>
isfx() ;
The "prefix" function ipfx (int) verifies that the stream is both ready and
willing to supply input, or at least to support calls to the streambuf
member functions described above. It also performs other initializiation
operations and, if you so request, will skip any white space in the input
stream. As a rule, formatted input functions skip leading white space while
unformatted input functions do not.
The "suffix" function isfx() performs any necessary wrapup opera-
tions after each input member function does its work. Some implementa-
tions define isfx () as an inline empty function - it often has nothing to
do. But that is not always the case for an arbitrary implementation. If you
write an input function that uses the istream object x and makes direct
calls to its associated streambuf object, always call x. ipfx (noskip) and
x. isfx() as shown above.
exception The prefix and suffix member functions have been part of iostreams for
handling many a year. You will find similar creatures as member functions of class
ostream. (See the next chapter.) But the draft C++ Standard introduces yet
another source of surprises, which calls for even more machinery: All sorts
of functions can throw exceptions. The input functions have clearly defined
responsibility when exceptions occur during their execution.
<istream> 175
If the Standard C++ library had complete control over matters, it would
not have to worry so much. Input functions could simply avoid calling
functions that might throw exceptions. But that is not the case. Any call to
rdbuf () ->sbumpc (), for example, can result in a call to a streambuf
virtual member function. Alld any object of class streambuf can actually
represent a derived class. In fact, it usually does. A derived stream buffer
is not necessarily part of the Standard C++ library: It can have program-
mer-supplied virtual member functions. The library can thus never know
when a calIon a streambuf member function might throw an exception.
The draft C++ Standard is clear about what happens when an exception
occurs during execution of an input (or output) member function. The
function must call setstate (badbit), then rethrow the exception. The
structure of an arbitrary input function must now look like:
try {
if (ipfx{noskip»
<perform any input>
isfx() ;
}
catch ( ••• ) {
setstate(badbit);
throw;
}
Note that the act of setting badbit can also raise an exception, of class
ios: : failure. (See page 123.) Should that happen, the original exception
never gets rethrown.
get Now you have enougll background to understand how the unformatted
input functions work. The simplest of these is the member function get ( ) .
It extracts a single character - probably by calling rdbuf ( ) - >sbwnpc ( )
inside the sandwich shown above - and delivers it as the value of the
function. A failure to extract the requested character, for any reason, is
reflected in a return value of EOF.
gCOWlt A failure by get () can also be detected another way: Each of the unfor-
matted input functions stores within the istream object a count of the
number of characters it extracts. You can access this stored value by calling
the member function gcount ( ) . In the case of get ( ), a subsequent call to
gcount () returns a count of either zero or one. Obviousl)', a subsequent
call to another unformatted input function for the same istream object
replaces the stored value with a newer one.
Equally obviously, the overhead in obtaining a single character via get ( )
is substantially higher than for the Standard C library function getchar ( ) ,
declared in <stdio.h>. The latter is almost invariably implemented as a
macro that fetches a character straight from an input buffer more often than
not. Thus, it behaves much like the member function streambuf::
sbwnpc ( ). But istream: : get () is almost impossible to treat similarl~
given the prefix/suffix calls and exception handling required by the draft
C++ Standard. You should thus favor methods that extract many more
cllaracters for each call, whenever possible, if performance is an issue.
176 Chapter 8
get You can also extract a single character by calling the member function
get (char& ). You call it instead of get () to store directly into a char. You
can also call it if you want to chain operations, as in:
is.get(cl).get(c2);
(a practice not universally admired). The variants get (unsigned char&)
and get (signed char&) behave the same as get (char), but store in the
other character types.
get The function get (char *, int, char) also has its variants for arrays
getline of unsigned char and signed char. You use it to extract a sequence of characters
up to, but not including, a delimiter character. If that sounds much like
getline (char *, int, char), which I described earlier, you're right. In
a nutshell, both deliver null-terminated strings guaranteed not to contain
an instance of the delimiter. Both set eofbi t if they encounter end-of-file
while extracting characters. Both set failbit if the buffer ends up holding
an empty string. But get doesn't actually consume the delimiter, while
getline does (without storing it). And get doesn't get upset if it fills the
buffer before it sees a delimiter, while getline sets failbit in this case.
(See the summary beginning on page 190.)
current This behavior of getline is not entirely consistent with current practice,
practice by the way: In at least some implementations:
• storing a null string in the buffer sets failbit only if no delimiter is
extracted and discarded
• filling the buffer exactly does not set failbit (if the first unstored
character is the delimiter, the buffer is not considered over full)
• encountering end-of-file sets both eofbit and failbit, not just the
former
Inconsistencies like this are a topic for on-going refinement by the Commit-
tee. Unless and until such issues are resolved, you are well advised not to
write code that is vulnerable to these differences.
read If you want rather less logic, consider the member function read (char
*, int) (or its alternate forms for arrays of signed char or unsigned char). It
simply extracts characters until the count you specify is exhausted, or until
no more characters can be extracted. Once again, it has slightly different
rules for setting status bits, which I discuss later in this chapter. (As before,
see page 190.)
writing Lest you think that iostreams are always at a performance disadvantage
stream buffers compared to Standard C input/ output, consider the member function
get (streambuf&, char). It extracts characters and writes them directly
to a stream buffer up to, but not including, a delimiter. Thus, you can write:
cin.get(*cout.rdbuf(), '\n');
to copy the next text line from cin directly to couto In this case, copying
can be quite fast. (A related extractor can be even faster for copying to the
end of the input stream, since it doesn't have to check for delimiters. I
describe it later in this chapter.)
<istream> 177
ignore A handful of additional member functions sometimes proves handy in
sync conjunction with the unformatted input functions. The member function
ignore (int, int), for example, extracts and discards characters up to a
specified count, or until a specified delimiter is extracted and discarded.
And the member function sync () simply calls the public "synchronizing"
function rdbuf ( ) ->pubsync () (assuming the associated streambuf object
is present).
Typically, such an operation flushes an output stream to the associated
external file. Some systems use calls like this to discard pending characters
from an interactive input stream, but that is not mandated by the draft c++
Standard. Don't count on it. For a pure input stream, there is no portable use
you can make of this particular member function.
peek Several member functions let you manipulate characters from the input
putback stream one at a time. The member function peek() lets you see the next
unget character to be extracted without actually consuming it. It is a safer interface
to rdbuf () ->sgetc (). Thememberfunctionputback(char) lets you "put
back" an extracted character - a safer rdbuf () ->sputbackc (). The next
extraction will deliver up the character you just put back. And the related
member function unget () backs up a character position without altering
the apparent contents of the input stream. It is a safer rdbuf ()->
sungetc (). With either of these last two functions, you shouldn't count on
backing up more than one character position between extractions. Portable
support for such antics is not guaranteed.
extractors Now for the formatted input functions, or extractors. These member
functions overload operator» for a left parameter of type istream&. That
lets you write code such as:
float £1;
cin » f1;
which extracts characters from the stream controlled by cin (the standard
input stream). Extraction continues so long as characters continue to match
the text pattern acceptable to the Standard C library function strtod,
declared in <std1ib.h>. The entire seq.uence extracted by this rule must
then be successfully converted, as if by calling strtod, and the resulting
value must be representable as type float.
If all those conditions obtain, the extractor stores the converted value in
f1 and succeeds. Otherwise, the function reports failure, typically by
setting fai1bi t in cin. In any event, all the characters extracted to make
up the valid text field (or valid prefix for such a field) are irretrievably
consumed.
If all that machinery sounds rather like fscanf, declared in <stdio. h>,
it should. Extractors serve the same function for the Standard c++ library
that the fscanf family serves for the Standard C libraI)T. You can, in fact,
implement extractors by calls to fscanf. That turns out to be not always
convenient, however:
778 Chapter 8
• The source of input for a stream controlled by an istream object is
controlled in tum by a streambuf object. You can't always relate that
source directly to the sources accessible by fscanf or sscanf.
• You can extract characters and store them in a buffer for processing by
sscanf. But doing so requires a buffer that is arbitrarily large. Or you
end up doing so much preprocessing of the text that you duplicate much
of the work done by sscanf.
For these reasons, it is not necessarily wise to make actual calls to f scanf
to implement extractors. Nevertheless, the definition of a typical extractor
member function in istream is in terms of fscanf conversion specifiers
(17.4.3.1). When in doubt, you can often tum to the C Standard for a more
precise description of extractor behavior.
character I begin with the extractors that perform a minimum of interpretation.
extractors These extract one or more characters from the input stream and deliver
them either to memory or to an output stream. The simplest of all is
operator> > (char&:), which extracts a single character. As usual, class
istream also defines unsigned char and signed char versions of the same
extractor.
These member functions are all similar in behavior to the member
function get (char&:) and its brethren, described earlier. The principal
difference internally is the argument to ipfx. An unformatted input func-
tion such as get ( char&:) should call ipfx (1) (or with some other nonzero
argument), to suppress skipping of leading white space.
The extractor, however, should call ipfx ( 0). That encourages ipfx to
skip leading white space before extracting the actual data to be delivered,
assuming the format flag skipws is set within the istream object. (It is set
by default when the object is constructed.)
ws If you want to skip white space before invoking an extractor, regardless
of the setting of skipws, you can always use the manipulator ws, declared
in <istream>. If you write:
cin » ws » fl;
then the manipulator always skips white space before fl is extracted. (It
doesn't hurt to try skipping white space more than once.)
string The member function operator> > ( char *) extracts a sequence of
extractors characters from the input stream and stores it as a null-terminated string
in the character array designated by the pointer argument. Yes, there are
three flavors, once again. This is one of the few extractors that uses the
width value stored in the istream object. If width () is zero, the width is
taken as INT_MAX, defined in <limits .h>. Input stops with the first white-
space character (which is not extracted), or when the specified number of
characters, counting the terminating null, are stored in the arra~ Note that
the width field is set to zero by this extractor, as is customary for functions
that make use of this field.
<istream> 179
stream buffer The member function operator» (streambuf&:) also strongly resem-
extractor bles one of the unformatted input functions shown earlier. You use it to
copy the remainder of the input stream to the output stream controlled by
the streambuf operand, as in: .
cin » cout->rdbuf();
As I mentioned before, this extractor version should be substantially faster
because it doesn't have to check for delimiters. Thus, it can move whole
blocks of characters at a time, if properly implemented.
scalar The remaining formatted input functions extract various scalar types.
extractors For all the integer types, you can control the assumed numeric base by how
you set the ios: : basefield group of format flags. (See page 126.) No
controls apply to the floating-point types (other than the usual ios: :
skipws) - integer, fixed-point, and scientific formats are all acceptable at
all times. Nor do any controls apply to the extractor for pointer to void, an
extractor of limited utility. (See page 192.)
user-defined You can, of course, also write your own extractors. It is commonplace,
extractors when designing a new class, to provide a tailored inserter at the very least.
If reading values of the class makes sense, then it is good manners to
provide an extractor as well. You might even want to write an extractor or
two that are not associated with a specific class.
The best style for writing new extractors is to do so in terms of the
member functions of class istream. If you must drop below this level and
access the associated streambuf object directl)', then by all means match
the discipline followed in the extractors presented here. If you don't, then
it's only a matter of time before you or one of your colleagues gets burned.
Such is the blessing, and the curse, of reusable software.
Footnotes:
Footnotes 92) The stream buffer can, of course, be associated with an input file, but it need
not be.
93) The function signatures ipfx (int) and i sfx () can also perfonn addi-
tional implementation-dependent operations.
94) See, for example, the function signature ws (istream&) .
95) See, for example, the function signature dec (ios&).
96) The effect of cin » ws is to skip any white space in the input sequence
controlled by cin.
188 Chapter 8
Future Directions
bool With the addition of the scalar type bool comes another formatted input
function for class istream. An integer 1 converts to "true," while a zero
converts to "false." The Committee has so far resisted the temptation to
accept any more elaborate spellings, such as T or true.
eofbit A later change regularizes the setting of eofbit when an extraction fails
(presumably signaling end-of-file). In the Informal Review Draft, only a
handful of the istream member functions promise to set this status bit as
needed. Hence, eofbit is an unreliable indicator here.
getline The member function getline has been altered to better match (at least
some) current practice. See page 176 for a description of the newer func-
tionalit)r.
locale As I mentioned earlier, the Committee has added locale objects. (See
objects page 32.) Extractors in class i stream are expected to access the locale object
imbued within the base ios subobject to determine how to parse input. In
principle, each istream object can operate within a different locale. By
default, however, the locale imbued by an istream constructor is the lIe ll
locale, not the current locale. Be warned.
wide-character I also repeat here the warning, first mentioned on page 119, that wide-
streams character streams have been added to the Standard C++ library. Class
istream now derives from a template class parameterized by the type of
the stream element. One instantiation, for type char, has essentially the same
functionality as described in this chapter. Another, for type wchar_t, sup-
ports streams of elements from some large character set.
Using <istream>
You have little or no occasion to include the header <istream> directly.
Like <ios> and <streambuf> in the previous two chapters, it is another
header invented by the Committee simply to split the iostreams declara-
tions into more manageable pieces. But the chances are just as good that
you'll use the contents of <istream> in a program. Class istream is the base
class for all classes that support extractions from a stream buffer, the classic
way to mediate all input in the iostreams package. The base class summa-
rizes what is common to all such input classes.
cin If you include the header <iostream>, you can use the istream object
cin to read the standard input stream. (See Chapter 14: <iostream>.)
Otherwise, you typically create an object derived from istream to mediate
input from a stream buffer you create at the same time. (See Chapter 11:
<strstream>, Chapter 12: <sstream>, and Chapter 13: <fstream>.)
constructor The only occasion you have for declaring an istream object directly is
to mediate input from a stream buffer created separatel)'. You can, for
example, set up a bidirectional stream various ways. I showed one way to
do so on page 122. The complementary way is:
<istrea.m> 189
#include <fstream>
ofstream ostr("abc", ios::in I ios::out);
istream istr(ostr.rdbuf(»;
or:
#include <fstream>
Implementing <istream>
istream Figure 8.1 shows the file istream, which implements the standard
header <istream>. Its principal business is defining the class istream, but
it also declares the manipulator ws. For a description of the type _Unin-
itialized, and the value _Noinit, see page 132.
_HAS_SIGNED_ Note that half a dozen of the member functions in class istream are
CHAR conditionally included. It turns out that several current implementations
of C++ have a common bug - they fail to treat char and signed char as
distinct types. Declaring all the required member functions produces am-
biguities, however spurious. Thus, the internal header <yxvals. h> defines
the macro _HAS_SIGNED_CHAR only when the types are properly distinct.
_Getffld The only other surprise should be the two protected (and secret) member
_Getifld functions _Getffld and _Getifld. These parse and gather character se-
quences for conversion to floating-point or integer values, respectively. I
describe them in greater detail below. For now, I focus on their rather
unusual declarations.
In both cases, the array of char parameter really is equivalent to pointer
to char. I chose this form, for both _Getffld and _Getifld, to emphasize
in each case that the size of the argument array is known and fixed. The
sizes depend on several macros defined in the internal header <yxvals. h>:
_MAX_EXP_DIG • _MAX_EXP_DIG - the number of decimal digits needed to represent the
largest floating-point exponent (a comfortably large value is 8)
196 Chapter 8
Figure 8.2: II istream -- istream basic members
#inc1ude <ctype.h>
istream.c #inc1ude <istream>
#inc1ude <ostream>
istream::-istream()
{ I I destruct an istream -- DO NOTHJ:NG
}
if (ch J= EOF)
rdbuf()->sputbackc(ch);
}
if (good ( »
return (1);
}
setstate(failbit);
return (0);
o
these functions takes a bit of effort in its own right. Remember, nothing
prevents a perverse user from generating an input sequence with 5,000
leading zeros, followed by a perfectly reasonable integer. You don't want
to blindly gather characters into a buffer as part of the extraction process. I
deal with this problem by doing a "smart" pre-parse.
<istream> 201
isxlong. c Figure 8.6 shows the file isxlong. c, which defines the member function
operator» (long&) . It extracts a long. It does indeed gather characters into
a buffer, but not blindly. The secret member function _Getifld extracts and
parses all integer fields. I revisit this workhorse function after presenting
all the other extractors that use it.
_MAX_J:NT_DJ:G Note that the buffer used by _Getifld has a bounded length, in this case
the value of the macro _MAX_J:NT_DJ:G, described earlier. For a typical
machine that represents long with 32-bits, a value of 16 is plenty big enough
202 Chapter 8
Figure 8. 10: II isxint -- istream::operator»{int&)
#include <limits.h>
isxint.c #include <istream>
istream& istream::operator»{int& i)
{ II extract an int
long 10;
*this » 10;
if (Igood{) II 10 < I~ MIN I lINT_MAX < 10)
setstate{failbit);
else
i = 10;
return (*this);
o
to deal with a sign, prefix, and enough significant digits to ensure overflow
if the value is indeed too large. I tend to make it 32, in the off chance that
the code has to handle even larger integers.
isxulong. c Figure 8.7 shows the file isxulong. c, which defines the member func-
tion operator» (unsigned long&). It extracts an unsigned long. The es-
<istream> 203
Figure 8. 13: II isxushort -- istream::operator»(unsigned short&)
#include <limits.h>
isxushor.c #include <istream>
sential difference between this and the previous extractor is the choice of
functions to convert the text sequence.
Extracting a pointer to void is a slightly different matter. It must, of
course, work in concert with its corresponding inserter for pointer to void.
Both should also convert an arbitrary representation for pointers, even if it
is bigger than the largest integer. You can, and probably should, tailor
pointer conversions for each implementation. What I show here is one way
to write a pointer extractor that is both portable and robust (but may not
always choose the most appropriate text representation for pointers).
isxpoint.c Figure 8.8 shows the file isxpoint.c, which defines the member func-
tion operator» (void *&:). It extracts a pointer to void. The trick it uses is
to access the pointer from a union, where it overlaps an array of unsigned
long. The extractor then extracts a series of integers separated by colons and
stores them in the union as integers. It calls _Getiffl with a nonzero
second argument to choose hexadecimal format, regardless of the fonnat
flags in hasef ield. The resultant pointer value is accessed from the union
in the end. So long as the corresponding inserter does the reverse, you can
be sure that a pointer value you extract matches the earlier one you inserted.
For a machine that represents pointer to void and unsigned long with the
same number of bytes, this extractor defaults to more conventional behav-
ior. It extracts a single unsigned long integer, with no colons, and converts it
by storage punning in the union. (This trick does not always yield the same
result as type casting between pointer and integer types, by the way: The
type cast has an opportunity to change representations, if it sees fit.)
isgetifl. c Figure 8.9 shows the file isgetifl. c, which defines the member func-
tion _Getifld. It carries out the work of gathering an integer field. This
function largely replicates the logic of fscanf, or even strtol, with an
important difference or two. It compresses all leading zeros to a single digit.
And it truncates a very big number at a value large enough to ensure
overflow, as I indicated above. It then counts on the calling function to call
strtol (for the long extractor) or strtoul (for the unsigned long extractor)
to do the rest of the conversion.
204 Chapter 8
Figure 8.14: II isxdoub1e -- istream::operator»(doub1e&)
#inc1ude <errno.h>
isxdoubl.c #inc1ude <stdlib.h>
#inc1ude <istream>
istream& istream::operator»(double& d)
{ II extract a double
_TRY_J:O_BEGJ:N
if (lipfx(»
setstate(fai1bit);
else
{ II gather characters and convert
char aC[_MAX_EXP_DIG + _MAX_SJ:G_DIG + 16];
char *ep;
errno 0; =
const double x =
_Stod(ac, &ep, _Getffld(ac»;
if (ep == ac I I errno 1= 0)
setstate{fai1bit);
else
d = x;
}
isfx() ;
_CATCH_XO_END
return (*this);
o
#if l_MWERKS_
#define NLONG 2 1* 9 * NLONG == max digits *1
if (n10 ==0)
return (0);
if (NLONG ==
1)
x = 10[1];
else
{
int i, n;
i sxint • c All the other integer extractors make use of either the long or the unsigned
isxuint.c long extractor. Figure 8.10 shows the file isxint.c, which defines the
isxshort. c member function operator» (int&). It extracts an into Figure 8.11 shows
isxushor.c the file isxuint.c, which defines the member function operator»(un-
signed int&). It extracts an unsigned into Figure 8.12 shows the file isx-
<istream> 205
Figure 8. 16: II isxfloat -- istream::operator»(float&)
#include <errno.h>
isxfloat.c #inc1ude <std1ib.h>
#inc1ude <istream>
istream& istream::operator»(float& f)
{ II extract a float
_TRY_iO_BEGiN
if (I ipfx () )
setstate(failbit);
else
{ II gather characters and convert
char aC[_MAX_EXP_DIG + _MAX_SiG_DiG + 16];
char *ep;
errno =
0;
const float x = _Stof(ac, &ep, _Getff1d(ac»;
if (ep == ac I I errno 1= 0)
setstate(fai1bit);
else
f = x;
}
isfx() ;
_CATCH_IO_END
return (*this);
o
and strtold, or something like them. Meanwhile, you can cop out by using
strtod to perform in place of these missing functions, as I show below. This
function is deficient in several ways, when used with the other two float-
ing-point precisions, but it meets many needs.
isxdoubl.c Figure 8.14 shows the file isxdoubl.c, which defines the member
function operator» (double&:). It extracts a double. It also gathers charac-
ters into a buffer with a bounded length, in this case determined by the
values of the macros _MAX_SiG_DiG and _MAX_EXP_DiG, described above.
For a typical machine with an SO-bit long double representation, you're
looking at 20 or so fraction digits and four exponent digits, plus the usual
assortment of signs, decimal point, and exponent character. I prefer to allow
for 36 fraction digits, since 112-bit floating-point significands are already
not uncommon. And I allow for a generous eight exponent digits.
The work of gathering a floating-point field is carried out by the pro-
tected member function _Getffld, which I describe below. It plays many
of the same tricks as its cousin, _Getifld, to keep the buffer length small
and bounded. The extractor itself then counts on the function _Stod to do
the rest of the conversion. _Stod behaves much like strtod, except that it
takes an additional argument computed by _Getffld - a power-of-ten
correction factor. The final converted value is what strtod would produce
from the compressed text field, times ten raised to the factor.
<istream> 209
}
if (_Chcount ==
0)
setstate(failbit);
*s = '\0';
isfx() ;
_CATCH_IO_END
return (*this);
o
xstod.c Figure 8.15 shows the file xstod.c, which defines a rough-and-ready
version of the function _Stade It simply scales the result returned by
strtod. For my enhanced implementation of the Standard C 1ibra~ I've
written a proper version of _Stod (and _Stof for type float and _Stold for
type long double, both of which I describe below). That library also calls these
functions from fscanf. The proper version does better error checking,
works faster, preserves precision more carefull~ and avoids any floating-
point overflows or underflows. But the machinery that makes all this
possible is too much of a distraction to show here.
isxfloat.c Figure 8.16 shows the file isxfloat.c, which defines the member
xstof.c function operator» (double&:). It extracts a float. And Figure 8.17 shows
the file xstof. c, which defines a rough-and-ready version of the function
_Stof that this extractor calls. It relies on _Stod to do the actual conversion,
then makes a weak attempt at detecting and reporting overflows. On some
machines, the program may trap out during the conversion of an oversize
value to float, so the checking may well be in vain.
isxldoub.c Figure 8.18 shows the file isxldoub.c, which defines the member
xstold.c function operator> >(long double&:). It extracts a long double. And Figure
8.19 shows the file xstold. c, which defines a rough-and-ready version of
the function _Stold that this extractor calls. Again,_Stod does all the work,
210 Chapter 8
Figure 8.24: II isgline -- istream::getline(char *, int, char)
#include <istream>
isgline.c
istream& istream::getline(char *8, int n, char delim)
( II get up through delimiter or count
_Bool copied = 0;
_Chcount = 0;
_TRY_IO_BEGIN
if (ipfx(l) && 0 < n)
{ II extract arbitrary characters
int ch,
for (; ; )
if (--n <= 0)
( II record count failure and quit
setstate(failbit);
break;
}
else if «ch = rdbuf()->sbumpc(» == EOF)
( II record eof and quit
setstate(eofbit);
break;
else
II count it and test for delim
++_Chcount;
if (ch == delim)
break;
*s++ = ch, copied = 1;
}
if (Icopied)
setstate(failbit);
*s = '\0';
isfx() ;
_CATCH_IO_END
return (*this);
o
but this time the possible failure is different. If long double has greater range
or precision than double on some architecture, this function will fail to
deliver it.
isgetffl.c Figure 8.20 shows the file isgetffl. c, which defines the member
function _Getffld. It carries out the work of gathering a floating-point
field. In many ways, it is simply a messier version of _Getifld, shown
earlier.
isget.c Now for the unformatted input functions. Figure 8.21 shows the file
isgchar.c isget .c, which defines the member function get (). It illustrates well the
difference between just extracting a character from a stream buffer (with
streambuf () ->sbumpc (») and doing so with all the checking mandated
for an istream member function. Figure 8.22 shows the file isgchar. c,
<istream> 211
Figure 8.25: II istread -- istream::read(char *, int)
#include <istream>
isread.c
istream& istream::read(char *s, int n)
{ I I get at most n bytes
_Chcount = 0;
_TRY_J:O_BEGJ:N
if (ipfx(l»
( II extract arbitrary characters
int m = rdbuf()->sgetn(s, n);
_Chcount += m;
if (m 1 = n)
setstate(failbit);
}
isfx() ;
_CATCH_J:O_END
return (*this);
o
else
II count it and test for delim
++_Chcount;
if (ch == delim)
break;
}
isfx() ;
_CATCH_J:O_END
return (*this);
o
212 Chapter 8
Figure 8.27: II isgstream -- istream::get(streambuf&, char)
#include <istream>
isgstrea.c
istream& istream::get(streambuf &sb, char delim)
( II get into streamhuf until delimiter
_Chcount =
0;
_TRY_IO_BEGIN
if (ipfx(l»
( II copy characters until failure
int ch,
for (; (ch rdbuf()->sbumpc(» 1= EOF; ++_Chcount)
_TRY_BEGIN
if (ch == delim I I sb.sputc(ch) c= EOF)
break;
_CATCH_ALL
break;
_CATCH_END
if (ch != EOF)
rdbuf()->sputbackc(ch);
}
if (_Chcount ==
0)
setstate(failbit);
isfx() ;
_CATCH_IO_END
return (*this);
o
isgstrea.c Similarl~ Figure 8.27 shows the file isgstrea.c, which defines the
member function get (streambuf&:, char). It follows the pattern estab-
lished earlier by isxstrea.c, again with only minor variations.
214 Chapter 8
Figure 8.32: II ws -- ws(istream&)
#include <ctype.h>
ws.c #include <istream>
ispeek.c The last group of istream member functions all calIon streambuf
isputbac.c public member functions to perform the critical operation. Figure 8.28
issync.c shows the file ispeek. c, which defines the member function peek ( ) .
isunget.c Figure 8.29 shows the file isputbac. c, which defines the member function
putback (char). Figure 8.30 shows the file i ssyne • c, which defines the
member function sync ( ) . And Figure 8.31 shows the file i sunget •c, which
defines the member function unget ( ) .
ws.c Finally, Figure 8.32 shows the file ws .c, which defines the manipulator
ws (istrea:m&) . I chose to replicate the logic in istream: : ipfx for skipping
white space rather than fiddling with the format flag skipws. Should an
exception occur while extracting characters, it's rather messy to restore the
format flags. I didn't want to introduce more ornate macros for exception
handling just to handle one or two situations like this.
I described the macro _CATCH_IO_ (x) on page 128. It performs the same
function as _CATCH_IO_END for a function that is not a member of class
istream.
Testing <istream>
tistream.c Figure 8.33 shows the file tistream.c. It tests the basic properties of
class istream. As in the previous chapter, I make use of classes derived
from istream and streambuf to do something concrete. This time, an
object of class istrstream helps construct a stream buffer (of class
strstreambuf) that delivers text from a constant character string in mem-
ory: (See Chapter 12: <sstream>.) The code is not completely portable
because it uses the macros _TRY_IO_BEGIN and _CATCH_IO_END, peculiar
to this implementation.
<istream> 215
The program defines the class Boolean, which stores an int value. It then
defines an extractor for this class, which maps N into a zero value and y into
a nonzero value. The program tests whether this extractor, and all the
member functions of class istream, exhibit at least some of their required
behavior.
If all goes well, the program prints:
SUCCESS testing <istream>
and takes a normal exit.
Exercises
Exercise8.1 For a call to istream::get(char *, int, char), write the predicates
(tests) that distinguish whether:
• the next character to extract is a delimiter
• the function encountered end-of-file
• the buffer is full
Exercise 8.2 For a call to istream: :getline(char *, int, char), write the predi-
cates (tests) that distinguish whether:
• the function extracted and discarded a delimiter character
• the function encountered end-of-file
• the buffer is full
Exercise 8.3 What kind of operations do you think istream: :isfx() might have to
perform?
Exercise 8.4 Write rules for the setting and clearing of the status flag eofbit by istream
member functions to make it more consistent. In particular, if eofbit is set,
istream: : get () should assuredly return EOF.
Exercise 8.5 Can you enforce the rules of the previous exercise if two or more istream
objects point at the same stream buffer? If so, describe how. If not, discuss
the structural changes required to make such enforcement possible.
Exercise 8.6 Does the fscanf family of functions have any capabilities not easily mod-
eled by istream extractors? Show how would you add any such missing
capabilities.
Exercise 8.7 Alter the function istream: :_Getffl (page 207) so that it need not return
a power-of-ten correction factor. Instead, it should produce only a null-ter-
minated string of bounded size that adequately represents the input field.
Exercise 8.8 [Harder] Write versions of _Stod (page 204), _Stof (page 205), and _Stold
(page 206) that deliver full accuracy, range, and exception checking for a
given architecture.
Exercise 8.9 [Very hard] Alter the functions you wrote for the previous exercise to work
properly on any architecture.
216 Chapter 8
Figure 8.33: II test <istream>
#include <assert.h>
tistream.c
#include <string.h>
Part 1 of 2 #include <iostream>
#include <istream>
#include <strstream>
II class Boolean
class Boolean {
public:
Boolean(int v)
: val (v) {}
int value() const
{return (val);
int value(int v)
{return (val = v);
private:
int val;
};
int maine)
{ II test basic workings of istream definitions
static const char input[] = °sl s2 s3"
abc"
" 1 23 -456 +7890 0012 00034 0"
" 12 +3.4567e+0004-8900e-2"
n N Y"
nrest of stream n ;
istrstream istrstr(input);
istream ins(istrstr.rdbuf(»;
assert (ins.good() && (ins.flags() & ios::showbase) O};
ins » showbase;
<istream> 217
Continuing assert«ins.f1ags() & ios::showbase) 1= 0);
/1 test string extractors
tistream.c
char sl[10];
Part 2 of 2 signed char s2[10];
unsigned char s3[10];
ins » sl » s2 » s3;
assert (strcmp(sl, "sl l1 ) == 0);
assert(strcmp«char *)s2, "s2") 0);
assert(strcmp«char *)s3, IIs3 11 ) 0);
/1 test character extractors
char ch,
signed char sch;
unsigned char uch;
ins » noskipws » ws » ch » sch » uch » skipws;
assert(ch == 'a' && sch == 'b' && uch == 'c');
/1 test integer extractors
int i;
long 10;
short sh;
unsigned int ui;
unsigned long u10;
unsigned short ush;
void *p;
ins » sh » hex » ush » oct » i » dec » ui;
assert{sh 1 && ush == Ox23 && i == -0456 && ui 7890);
ins » 10 » ulo » p;
assert{lo 12 && ulo == 34 && P == 0);
/1 test floating-point extractors
double db;
float £1;
long double Idb;
ins » f1 » db » 1db;
assert{fl == 12. && db == 34567. && Idb -89.);
/1 test Boolean extractor
Boolean no(O), yes(l);
assert (no.value() 0 && yes.va1ue() 1);
ins » yes » no;
assert (no.va1ue() 1 && yes.value() 0);
/1 test streambuf extractor and get functions
char buf[20], sbuf[20], ubuf[20);
ostrstream ostr;
ins » *ostr.rdbuf();
assert (ins.good() && ins.get() EOF) ;
ins.rdbuf(ostr.rdbuf(»;
ins.get{buf, sizeof (buf), '0');
assert {strcmp(buf, "rest II) == 0);
ins.get(sbuf, sizeof (sbuf), 's');
assert{strcmp«char *)sbuf, "of II) 0);
ins.get(ubuf, sizeof (ubuf»;
assert{strcmp«char *)ubuf, I1stream") == 0);
cout « "SUCCESS testing <istream>" « endl;
return (0);
D
·Chapter8
Chapter 9: <ostream.>
Background
The principal business of the header <ostream> is to define the class
ostream. This class is the obvious complement to class istream, which I
described in the previous chapter. I have intentionally modeled this chapter
after its predecessor, to highlight the structural similarities.
ostream Class ostream is derived from the virtual public base class ios to help
you insert characters into a stream. The best known object of this class is
cout, which controls output to the standard output stream - the same C
stream controlled by stdout, declared in <stdio.h>. You can also create
ostreamobjects that control files you open by name, or strings of text stored
in memol)T, as later chapters reveal.
Most of the ostream member functions fall into one of two broad
categories:
• unformatted output functions, which insert sequences of arbitrary char
values, such as uninterpreted text or binary data
• formatted output functions, which convert encoded values of the vari-
ous scalar and string data types and insert the resultant sequences
unformatted The former group uses the name put and overloads the name write. It
output is analogous to the Standard C library's fputc and fwrite, declared in
<stdio.h>, but a bit easier to use. For example, you can insert an arbitrary
character sequence with:
if (cout.write(buf, n»
<output succeeded>
The test is true (nonzero) only if the member function successfully inserts
all n characters from buf to the stream controlled by the ostream object
couto
formatted The latter group of member functions overloads operator« to make
output the basic family of inserters. It is analogous to the Standard C library's
fprintf and friends, but with a variety of advantages. For example, you
can insert an octal integer with:
int n;
if (cout « oct « n)
<output succeeded>
220 Chapter 9
The test is true only if the second inserter inserts all the characters required
to represent the int value n as an octal integer in text form.
Inserters are the single most popular selling point for iostreams over
more conventional Standard C input and output. (Extractors are a close
second.) But they are easier to explain after you gain more familiarity with
the unformatted output functions. I return to inserters later in this section.
Remember that all extractions and insertions in iostreams are mediated
by objects of class streambuf. (See Chapter 7: <streambuf>.) An object of
class ostream finds its related streambuf object via a pointer in its base
subobject of class ios. A member function of class ostream can then insert
an output character by calling rdbuf () ->sputc ( ).
opfx There is one small problem, however. It is perfectly permissible for the
osfx pointer to streambuf to be null. It is also quite possible that the pointer is
non-null, but the stream is in some error state that should discourage
inserting. Thus, it behooves any output function to look before it leaps. The
canonical way to play safe is to wrap every inserter member function with
two calls to ostream member functions:
if (opfx(»
<perform any output>
osfx() ;
The "prefix" function opfx (int) verifies that the stream is both ready and
willing to accept output, or at least to support calls to the streambuf
member functions. It also performs other initializiation operations.
unit The "suffix" function osfx () performs any necessary wrapup opera-
buffering tions after each output member function does its work. In particular, if the
format flag unitbuf is set, the function flushes the associated stream buffer.
Such "unit buffering" is often a happy compromise between the better
performance of full buffering and the tighter synchronization with external
files of no buffering at all. If you write an output function that uses the
ostream object x and makes direct calls to its associated streambuf object,
always call x.opfx() and x.osfx() as shown above.
exception Inserters must also handle exceptions, for much the same reasons that
handling extractors do. (See page 174.) Any call to rdbuf ( ) - >sputc () can result in
a call to the virtual member function streambuf : : overflow, which can be
a programmer-supplied virtual member function for a derived class. So as
with extractors, the library can never know when a calIon a streambuf
member function might throw an exception.
The draft c++ Standard says that when an exception occurs during
execution of an output (or input) member function, the function must call
setstate(badbit), then rethrow the exception. The structure of an arbi-
trary output function must now look like:
try {
if (opfx(»
<perform any output>
osfx() ;
}
<ostream> 221
catch ( ••• ) {
setstate(badbit);
throw;
}
Note once again that the act of setting badbit can also raise an exception,
of class ios: : failure. (See page 123.) Should that happen, the original
exception never gets rethrown.
put Now you have enough background to understand how the unformatted
output functions work. The simplest of these is the member function
put (char) . It inserts a single character - probably by calling the stream-
buf member function rdbuf () ->sputc (char) inside the sandwich shown
above - and delivers it as the value of the function. A failure to insert the
requested character, for any reason, is reflected in a return value of EOF.
Obviousl)', the overhead in inserting a single character via put (char) is
substantially higher than for the Standard C library function put-
char(int), declared in <stdio.h>. The latter is almost invariably imple-
mented as a macro that stores a character straight into an output buffer
more often than not. Thus, it behaves much like the member function
streambuf:: sputc (char). But ostream: : put (char) is almost impossi-
ble to treat similarl)', given the prefix/suffix calls and exception handling
required by the draft C++ Standard. You should thus favor methods that
insert many more characters for each call, whenever possible, if perform-
ance is an issue.
write If you want rather less logic per character, consider the member function
write (const char *, int) (or its alternate forms for constant arrays of
signed char or unsigned char). It simply inserts characters until a count is
exhausted, or until no more characters can be inserted.
reading For still faster copying from another stream buffer, consider the inserter
stream buffers operator< < (streambuf&) . Actually a formatted input function, it extracts
characters from its stream buffer argument and writes them directly to the
stream buffer controlled by the ostream object. Thus, you can write:
cout « *cin.rdbuf();
to copy the remaining characters from cin directly to couto In this case,
copying can be quite fast. Related istream member functions can copy up
to, but not including, a delimiter you specify. (See the summary starting on
page 190.)
flush The member function flush () simply calls the public "synchronizing"
function rdbuf () ->pubsync ( ) (assuming the associated streambuf object
is present). Typicall)', such an operation flushes an output stream to the
associated external file. Several mechanisms exist to ensure that synchro-
nization occurs at judicious times:
• You can create a stream buffer that is unbuffered. (See, for example,
Chapter 13: <fstream>.)
• You can set the format flag unitbuf, described above, to flush output
after every call to a formatted or unformatted output function.
222 Chapter 9
• You can use the manipulator endl, described below, to both insert a
newline character ('\n') and flush output.
• You can tie the ostream object to another istream or ostream object, to
flush output on any call to a prefix function for that other object. (See
page 125.)
And, of course, the stream buffer will be flushed when it is destroyed. You
need call flush () for an ostream object only if you can't be certain that
timely synchronization will occur in the normal course of affairs.
inserters Now for the rest of the formatted output functions, or inserters. These
member functions overload operator« for a left parameter of type
ostream&. That lets you write code such as:
float fl;
cout « fl;
which converts the float value fl to a character sequence and inserts those
characters into the stream controlled by cout (the standard output stream).
The character sequence is essentially the same as that produced by the
Standard C library function fprintf, declared in <stdio.h>. The entire
sequence must be successfully inserted in the stream buffer for the function
to succeed. Otherwise, the function reports failure, typically by setting
badbit in couto
Thus, inserters serve the same function for the Standard C++ library that
the fprintf family serves for the Standard C library. You can, in fact,
implement inserters by calls to fprintf. That turns out to be not always
convenient, however:
• The destination for output from a stream controlled by an ostream object
is controlled in tum by a streambuf object. You can't always relate that
destination directly to the destinations writable by fprintf or sprintf.
• You can store characters into a buffer using sprintf then insert them
into the stream buffer. But doing so requires a buffer that is arbitrarily
large. Or you end up doing so much postprocessing of the text that you
duplicate much of the work done by sprintf.
• The program can specify an arbitrary fill character for an inserter, not
just the implied space character supplied by fprintf. Moreover, the
program can specify that this arbitrary character be used for "internal"
fill, in place of the implied zero (, 0') supplied by fprintf.
For these reasons, you can't count on calls to fprintf to do all the hard
work of implementing inserters. Nevertheless, the definition of a typical
inserter member function in ostream is in terms of fprintf conversion
specifiers (17.4.4.1). When in doubt, you can often tum to the C Standard
for a more precise description of inserter behavior.
character I begin with the inserters that perform a minimum of interpretation.
inserters These insert one or more characters from the output stream and deliver
them either to memory or to an output stream. The simplest is opera-
<ostream> 223
tor« (char&:), which inserts a single character. As usual, class ostream
also has unsigned char and signed char versions of the same inserter.
adjustfield These three inserters share one peculiar property: Of all the formatted
fill output functions defined in terms of fprintf conversions, only these
width ignore the field width. All other such inserters determine a minimum field
width by calling width ( ) . If the conversion results in fewer characters, the
result is padded with the fill character, defined by fill (). Where the
padding goes is determined by the format flags in the group adj ustf ield.
(See the summary of formatting information beginning on page 124.) But
the three character inserters each insert only a single character, never
padding it. Equally, none of these three inserters set the field width to zero,
by calling width ( 0) , as all others do.
These member functions are all identical in behavior to the member
function put (char), described earlier.
endl If you want to insert a newline character (, \n') to terminate a text line,
flush you should favor the manipulator endl, declared in <ostream>. If you
write:
cout « fl « endl;
then the manipulator inserts a newline character after f1 is inserted. It also
flushes the stream buffer, as I indicated earlier. For an output stream that
might be associated with an interactive device, this is probably desirable
behavior. You can also flush the output stream, without inserting a charac-
ter, with the manipulator flush.
ends You can insert a null character (without flushing the output stream) with
the manipulator ends. You seldom have occasion to write a null character
to a text file - null characters are displayed, printed, and transmitted
haphazardly by devices that expect only human-readable text. But a com-
mon use for an ostream object is to mediate output to a stream buffer that
constructs an in-memory character sequence. Such a sequence often wants
a terminating null character. The manipulator ends provides highly visible
evidence that the null character is indeed being supplied.
string The member function operator« (const char *) inserts a sequence
inserters of characters into the output stream from a null-terminated string in the
character array designated by the pointer argument. Yes, there are three
flavors, once again. And yes, you can pad a string on either end with fill
characters.
scalar The remaining formatted output functions insert various scalar types.
inserters For all of these inserters, you can specify a field width, a fill character, and
the format flags in the group adjustfield. These work together to deter-
mine whether and where to insert fill characters as padding when gener-
ating the text representation of a value. The scalar inserters further subdi-
vide into integer types, floating-point types, and pointer to void. You control
the behavior of each of these groups with different subsets of the format
flags.
224 Chapter 9
user-defined You can, of course, also write your own inserters. It is commonplace,
inserters when designing a new class, to provide a tailored inserter at the very least.
If reading values of the class makes sense, then it is good manners to
provide an extractor as well. You might even want to write an inserter or
two that are not associated with a specific class.
The best style for writing new inserters is to do so in tenns of the member
functions of class ostream. If you must drop below this level and access the
associated streambuf object directl)', then by all means match the discipline
followed in the inserters presented here. Extractors are more fragile than
inserters, because of the need to look ahead in the input sequence and push
back an occasional character. But inserters are delicate enough in their own
right. Don't take unnecessary chances.
Footnotes:
Footnotes 97) The stream buffer can, of course, be associated with an output file, but it
need not be.
98) The conversion specification #0 generates a leading 0 which is not a
padding character.
99) The function signatures opfx () and osfx () can also perform additional
implementation-dependent operations.
1(0) See, for example, the function signature endl (ostream&:).
101) See, for example, the function signature: :dec (ios&:).
102) This behavior differs from that for istream:: istream&: opera-
tor» (streambuf&:), which does not rethrow the exception.
103) The effect of executing cout « endl is to insert a newline character in
the output sequence controlled by cout, then synchronize it with any
external file with which it might be associated.
104) The effect of executing ostr « ends is to insert a null character in the
output sequence controlled by ostr. If ostr is an object of class
strstreambuf, the null character can terminate an NTBS constructed in
an array object.
230 Chapter 9
Future Directions
boo1 With the addition of the scalar type boo1 comes another formatted
output function for class ostream. A "true" value converts to an integer I,
while "false" becomes zero.
locale Again I remind you that the Committee has added locale objects. (See
objects page 188.) Inserters in class ostream are expected to access the locale object
imbued within the base ios subobject to determine how to produce output.
In principle, each ostream object can operate within a different locale. By
default, however, the locale imbued by an ostream constructor is the "C"
locale, not the current locale.
wide-character I also repeat here the warning, first mentioned on page 119, that wide-
streams character streams have been added to the Standard C++ library. Class
ostream also becomes a template class parameterized by the type of the
stream element. One instantiation, for type char, has essentially the same
functionality as described in this chapter. Another, for type wchar_t, sup-
ports streams of elements from some large character set.
Using <ostream>
You have little or no occasion to include the header <ostream> directly.
Like <ios>, <streambuf>, and <istream> in the previous three chapters,
it is another header invented by the Committee simply to split the iostreams
declarations into more manageable pieces. But the chances are just as good
that you'll use the contents of <ostream> in a program. Class ostream is the
base class for all classes that support insertions into a stream buffer, the
classic way to mediate all output in the iostreams package. The base class
summarizes what is common to all such output classes.
cout If you include the header <iostream>, you can use the ostream object
cerr cout to write the standard output stream. (See Chapter 14: <iostream>.)
clog Similarl~ you can use the ostream objects cerr or clog to write the
standard error stream. Otherwise, you typically create an object derived
from istream to mediate output to a stream buffer you create at the same
time. (See Chapter 11: <strstream>, Chapter 12: <sstream>, and Chapter
13: <fstream>.)
constructor The only occasion you have for declaring an ostream object directly is
to mediate output to a stream buffer created separately. You can, for
example, set up a bidirectional stream various ways. I repeat here the way
I showed on page 122:
#inc1ude <fstream>
ifstream istr{"abc", ios::in I ios::out);
ostream ostr{istr.rdbuf{»;
or:
#inc1ude <fstream>
<ostream> 231
ifstream istr(Dabc", ios::in I ios::out);
ostream ostr;
ostr.rdbuf(istr.rdbuf(»;
Given an ostream object x, you can perform a whole host of operations
on it. Many are inherited from the base class ios, which I have already
described. (See Chapter 6: <ios>.) You can also call:
• predefined manipulators, such as x « end1, to alter the state of the output
sequence or values stored in x
• character output functions, such as x.put (), to insert sequences of one or -
more characters
• scalar inserters, such as x « n, for an int n, to convert an internal
representation to a sequence of characters that represents its value and
insert the seeuqnce
• support functions, such as x. flush ( ), to help you write your own ma-
nipulators and extractors
I deal with these groups of operations in order.
predefined All the manipulators defined for class ios also apply to objects of class
manipulators ostream, naturally enougll. The same is true for the ios member functions
that have the same effect as the manipulators. (See the lengthy discussion
of formatting information beginning on page 124.) Not all have meaningful
effect, however. For an ostream object, the relevant information depends
on the scalar type being inserted. I describe the particulars later, with the
inserters for each group of types. One flag affects all formatted and unfor-
matted output functions:
unitbuf • the format flag unitbuf, which calls for the output stream to be flushed
by a call to the suffix function ostream: : osfx ( )
The following formatting information affects all the string and scalar
formatted output functions, except for the three character types:
width. the field width, which determines the minimum number of characters to
insert
fill • the fill character, which determines the character to add repeatedly to
meet the minimum width requirement
adjustfield • the format flags in the adjustfie1d group, which determine whether
fill characters are added at some internal point in the conversion (in-
ternal), before the conversion (right), or after the conversion (left)
The "internal" point in a numeric conversion is generally where you would
add leading zeros, or those * characters on checks. For a hexadecimal
conversion, it is to the right of the Ox or ox prefix. For all others, it is
immediately before the leading digit.
manipulators The header <ostream> declares two peculiar member functions:
ostream& operator«(ostream& (*pf)(ostream&»;
ostream& operator«(ios& (*pf)(ios&»;
I described the istream version of the second of these on page 124. It is the
magic that makes the manipulators defined for class ios work as "pseudo-
232 Chapter 9
extractors" with ostream objects. The first supports manipulators intended
to work only with ostream objects.
Three such creatures are defined by the Standard C++ library, all de-
clared in <ostream>. (But see the template class omanip in Chapter 10:
<iomanip>.) You can write:
endl x« endl;
to insert a newline character (, \n') and flush the output stream. This
inserter is both a convenient shorthand and a useful way to synchronize
the output stream periodically with an interactive file, such as a terminal
display:
ends Similarl)', the manipulator ends inserts a null character (, \0') without
flushing the stream. You use this manipulator primarily to insert a termi-
nating null character after generating an in-memory character sequence.
(See Chapter 11: <strstream>.)
flush The manipulator flush flushes the output stream without inserting a
character. You use this manipulator only when you can't be certain that a
necessary synchronization has occurred. (See the discussion on page 221.)
character Class ostream provides several member functions for inserting se-
output quences of one or more characters. Here is my attempt to characterize them
functions all in a way that highlights their critical differences. Each of the member
functions that follows:
width - pads with fill characters to the specified field width, then sets the field
width to zero, only if its description says "pads to field width"
badbit - sets badbit if it fails to insert all characters
With those blanket rules in mind, here is the list of functions you might use
to insert sequences of characters:
operator« - operator«(char ch), operator«(signed char ch), opera-
tor< < (unsigned char ch) - inserts ch
operator« - operator«(const char *s) - (n is strlen(s), where strlen is
declared in <string .h» inserts n characters beginning at s, pads to field
width
operator« operator« (streambuf&: sb) - extracts remaining characters from
-
the streambuf sb (and inserts them in *this), stops on an insertion
failure (character not extracted) or on any exception (exception is re-
thrown), also described on page 191
put - put (char ch) - inserts ch
write - write(const char *s, int n),write(const signed char *s, int
n), write (const unsigned char *s, int n) - inserts n characters
beginning at s
As with the character input functions (page 190), I can't counsel you on
which of these member functions to use. That decision depends heavily on
the particular need, and the style of programming you favor. I do suggest
as before, however, that you pick one or two of these choices, learn them
well, and stick with them. The options are nowhere near as rich as for
<ostream> 233
extracting character sequences, but it's still too easy to forget details if you
try to use them alL
scalar The scalar inserters in class ostream are important work horses. Each
inserters converts some encoded form within a C++ program to a human-readable
text sequence (or field). The form is determined by type T in a member
declaration of the form:
ostream& operator«(T X)i
where x designates the value of the argument expression.
In all cases, the following steps occur, in order:
- If any of the status bits badbit, eofbit, or failbit is set, the function
makes no attempt to insert characters.
- If tie () is not a null pointer, the function flushes the specified stream.
- The function generates the minimum-length character sequence that
represents the argument value, as determined by the argument type and
any relevant format flags.
- If the field width is greater than the length of this character sequence,
the function pads the character sequence with fill characters as deter-
mined by the format flags in the adjustfield group.
- If the function cannot insert all the characters in the generated field, it
sets badbit.
- If the format flag uni tbuf is set, the function flushes the output stream.
In all cases, the function returns *this, so that inserters can be chained.
integer Integer inserters exist for the types char, signed char, unsigned char, short,
inserters int, unsigned int, long, and unsigned long. You cannot insert an integer value
directly from a an expression of a character type. An inserter for a character
type inserts the character code instead, as I described earlier. You must type
cast the character expression ch to some integer type, as with (int) ch.
Remember that the basefield format flags determine the base for both
input and output conversions. For inserters only, clearing all basefield
flags calls for decimal output.
For all the integer types, you can control:
basefield - the numeric base for the text representation by how you set the
ios: :basefield group of format flags (see page 126)
showpos - whether non-negative values have a leading +, by setting the format flag
showpos
showbase • whether octal values have a leading 0 or hexadecimal values have a
prefix Ox or ox, by setting the format flag showbase
uppercase - whether hexadecimal values use uppercase letters for x and the digits a
through f, by setting the format flag uppercase
adjustfield - whether padding occurs after any sign and/ or prefix and before the
digits, by setting just the format flag internal in the group adjust-
field
234 Chapter 9
floating-point Floating-point inserters exist for the types float, doublet and long double.
inserters They generate the same formats as the fprintf conversion specifiers e, f,
and g (or their uppercase versions). For niggling details, see a precise
description of that function (such as in P&B92).
For all the floating-point types, you can control:
showpos - whether non-negative values have a leading +, by setting the format flag
showpos
showpoint - whether to preserve the decimal point along with any trailing zeros, by
setting the format flag showbase
uppercase - whether to use an uppercase letter for e, by setting the format flag
uppercase
adjustfield - whether padding occurs after any sign and before the digits, by setting
just the format flag internal in the group adjustfield
floatfield - whether to include a decimal exponent, by setting just the format flag
scientific, or to omit a decimal exponent, by setting just the format
flag fixed, or to choose the format that best represents the value of the
number, by setting any other combination of flags in the group float-
field
Specificall~ the three formats selected by the group floatfield follow the
same rules as for the fprintf conversion specifiers e (scientific), f
(fixed), and g (any other).
pointer You can use the inserter operator«(void *) to convert an arbitrary
inserter object pointer. The draft C++ Standard defines this conversion in terms of
thep conversion specifier for fprintf, but the C Standard has little to add
on that topic. Certainl~ whatever this inserter generates should be accept-
able to its corresponding istream extractor, and should recover the same
value. (See page 192.) But little is promised about how pointers appear as
character sequences.
Often, an implementation displays a pointer to void as one or more
hexadecimal integer values. And often, the conversion is affected by the
same flags that qualify hexadecimal conversions. But the draft C++ Stand-
ard is mum on this topic. Don't count on much in the way of portable
behavior when inserting pointers. (I use such inserters only for debugging.)
And make a point of writing an explicit (void *) type cast before any
pointer you wish to insert. A pointer to char will certainly get hijacked by
another inserter. And you never know when someone else might decide to
write an inserter that accepts some other object pointer type.
support The remaining member function in class ostream is the sole "support
functions function" (unlike class istream, which has several). You call it when you
want to take more direct cnntrol of the associated stream buffer, as when
defining your own inserters (see page 179) or your own manipulators.
flush You call the member function flush () simply to synchronize the output
stream with any external file. I discussed it and related operations earlier
in this chapter, on page 221.
<ostrea.m> 235
Generating output is a much simpler task than parsing input, but I still
counsel you to keep such antics to a minimum. The member functions I
have described so far also represent a considerable amount of engineering
in their own way, just like extractors. You are more likely to violate some
subtle semantic constraints if you strike off on your own. So wherever
possible, I urge you to write new inserters and manipulators in terms of
existing ones.
stream An alternative way to write your own inserters is to model them after
buffer the ones I show here. Follow the pattern I showed on page 220. Better yet,
primitives if you're using this implementation of the Standard C++ libral}', follow the
revised pattern I show on page 241. Note the guidelines for calling the
public member functions of class streambuf, starting on page 160. And,
wherever possible, find a working inserter similar to the one you are
writing and follow its structure.
locales As a final note, I remind you that the ostream member functions exhibit
some behavior that depends upon the current locale:
- A "decimal point" in a floating-point conversion is the character 10-
caleconv() ->decimalj)Oint [0], where localeconv is declared in
<locale.h>.
- Floating-point inserters may generate alternate formats outside the IICII
locale.
If your program alters the current locale by calling setlocale, declared in
<locale.h>, the behavior of some inserters can thus change. Moreover,
future versions of iostreams may behave differently. (See page 188.)
Implementing <ostream>
ostream Figure 9.1 shows the file ostream, which implements the standard
header <ostream>. Its principal business is defining the class ostream, but
it also declares the manipulators endl, ends, and flush. For a description
of the type _Uninitialized, and the value _Noinit, see page 132. For a
description of the macro _HAS_SIGNED_CHAR, see page 195.
Five protected functions with secret names perform formatted output:
_Ff - _Ff converts the floatfield flags into an index value (0, 4, or 8) for
floating-point conversions.
_If - _If converts the basefield flags into an index value (0, 4, or 8) for
integer conversions.
_Pad - _Pad inserts the characters generated by _Print, plus any repetitions of
the fill character or zero digits (0).
_Pr - _Pr determines the (bounded) precision to use for floating-point conver-
sions.
_Print - _Print assembles the arguments for a call to vsprintf, declared in
<stdio .h>, then calls _Pad to insert the padded character sequence.
236 Chapter 9
Figure 9.1: II ostream standard header
#ifndef _08TREAM_
ostream #define _08TREAM_
Part 1 of 2 #include <streambuf>
II class ostream
class ostream : virtual public ios
public:
ostream(streambuf *_8)
: ios (_8) {}
ostream(_Uninitialized)
: ios(_Noinit) {}
virtual -ostream();
_Bool opfx();
void osfx();
ostream& operator«(ostream& (*_F)(ostream&»
{return «*_F) (*this»; }
ostream& operator«(ios& (*_F) (ios&»
{(*_F) (*(ios *)this); return (*this);
ostream& operator«(const char *);
ostream& operator«(char _C)
{putt_C); return (*this); }
ostream& operator«(unsigned char _C)
{return (*this « (char)_C); }
ostream& operator«(short _X)
{return (_print(&"B hoB hxB hd"[_:If()], _X» ;
ostream& operator«(unsigned short _X)
{return (_Print(&IIB hoB hxB hu" [_J:f()], _X»;
ostream& operator«(int _X)
{return (_Print(&IIB oB xB dll[_J:f()], _X» ;
ostream& operator«(unsigned int _X)
{return (_Print(&"B oB xB ulI[_:If()], _X»;
ostream& operator«(long _X)
{return (_Print(&"B loB lxB ld"[_:If()], _X»;
ostream& operator«(unsigned long _X)
{return (_print(&IIB loB lxB lUll [_J:f()], _X»;
ostream& operator«(float _X)
{return (_Print (&IIP. eP. fP. gll[_Ff()], _Pr() , _X»;
ostream& operator«(double _X)
{return (_print(&"P.leP.lfP.lglI[_Ff()], _Pr() , _X»;
ostream& operator«(long double _X)
{return (_Print(&IIP.LeP.LfP.Lg"[_Ff()], _Pr(), _X»;
ostream& operator«(void *);
ostream& operator«(streambuf&);
ostream& put(char);
ostream& write(const char *, int);
ostream& write(const unsigned char *_S, int _N)
{return (write«const char *)_8, _N»; }
ostream& flush();
#if _HAS_8J:GNED_CHAR
ostream& operator«(signed char _C)
{return (*this « (char)_C); }
ostream& write(const signed char *_8, int _N)
{return (write«const char *)_8, _N»; }
#endif 1* _HAS_8J:GNED_CHAR *1
<ostream> 237
Continuing protected:
int _Ff()
ostream (return «flags() & floatfield) == scientific? 0
Part 2 of 2 : (flags() & floatfield) == fixed? 4 : 8); }
int _1f()
(return «flags() & basefield) == oct? 0
: (flags() & basefield) == hex? 4 8);}
void _Pad(const char *, char *, int);
int _Pr();
ostream& _Print(const char *, ••• );
};
II manipulators
ostream& endl(ostream&);
ostream& ends(ostream&);
ostream& flush(ostream&);
#endif o
_Boo10stream::opfx()
( II setup for output
if (good() &:& tie() 1= 0)
tie()->f1ush();
return (good(»;
void ostream::osfx()
( I I wrapup after output
if (f1ags() & unitbuf)
f1ush();
o
ostream. c Figure 9.2 shows the file ostream. c. It defines the three functions you
are likely to need any time you declare an object of class ostream, its
destructor, opfx ( ), and osfx ( ) . Note the use of the call tie ( ) to access the
<ostream> 239
else
{ II put internal fill as leading fill
if (lrep(rdbuf(), fill(), np)
I I !send(rdbuf(), s, ni)
I I !rep(rdbuf(), '0', nz)
II 1 send (rdbuf (), sf, n - ni»
ok = 0;
}
width(O);
if (10k)
setstate(badbit);
int ostream::_Pr()
{ II get bounded precision
return (precision() <= 0 &:&: l(flags() &: fixed) ? 6
_MAX_SIG_DIG < precision() ? _MAX_SIG_DIG
: precision(»;
o
argument list that vsprintf expects is the list assembled for the call to
_Printf. For a description of the macros _MAX_EXP_DIG and
_MAX_SIG_DIG,see page 195. These are used, much as in several istream
member functions, to define a safe buffer size for the conversion.
ospad. c Figure 9.5 shows the file ospad.c, which defines the member functions
_Pad and _Pro The latter computes the bounded precision used by a
floating-point conversion, as I indicated earlier. The macro _MAX_SIG_DIG
establishes the upper bound, which should be large enough to capture all
meaningful precision on a conversion by vfprintf.
Function _Pad is called by _Print to finish a formatted output operation,
as I also outlined above. The logic is, to put it mildl)', intricate. It must:
• determine whether any zero digits were suppressed by bounding the
precision earlier, then determine where to inject any such digits
• determine whether any fill characters need be added, then determine
where to inject any such padding
To keep the logic more or less readable, the code treats separately each of
the five(!) places where fill characters can be injected. It also inserts all
characters by calling one of two inline functions local to this translation
unit:
<ostream> 243
Figure 9.8: II osistream -- ostream::operator«(streambuf&)
#include <ostream>
osistrea.c
ostream& ostream::operator«{streambuf& sb)
( I I insert from streambuf
_Bool copied =
0;
_TRY_IO_BEGIN
if (opfx(»
{ II copy characters until failure
char buf[512];
int n;
for (; 0 < (n = sb.sgetn(buf, sizeof (buf»);
copied =
1)
_TRY_BEGIN
if (rdbuf()->sputn(buf, n) 1= n)
break;
_CATCH_ALL
setstate(failbit);
_RERAISE;
_CATCH_END
}
if (Icopied)
setstate(failbit);
osfx() ;
_CATCH_IO_END
return (*this);
o
osflush.c Figure 9.9 shows the file osf lush. c, which defines the member function
oswrite.c flush(). And Figure 9.10 shows the file oswrite.c, which defines the
member function write (const char *, int). Both simply call the ap-
propriate streambuf public member function to perform the critical opera-
tion in each case.
end1.c Now for the three manipulators that work only with ostream objects.
ends.c Figure 9.11 shows the file end1. c, which defines the function end1. Figure
f1ush.c 9.12 shows the file ends.c, which defines the function ends. And Figure
9.13 shows the file flush. c, which defines the function flush. All three are
straightforward.
<ostream> 245
Figure 9.12: II ends -- ends(ostream&)
#include <ostream>
ends.c
ostream& ends(ostream& os)
{ II te~inate output string
os.put('\O');
return (os);
o
Testing <ostream>
tostream.c Figure 9.14 shows the file tostream.c. It tests the basic properties of
class ostream. As in the previous chapter, I make use of classes derived
from ostream and streambuf to do something concrete. This time, an
object of class ostrstream helps construct a stream buffer (of class
strstreambuf) that accumulates a character string in memory: (See Chap-
ter 11: <strstream>.) The code is not completely portable because it uses
the macros _TRY_IO_BEGIN and _CATCH_IO_END, peculiar to this imple-
mentation.
The program again defines the class Boolean, as does tistream.c,
which stores an int value. It then defines an inserter for this class, which
maps a zero value into N and a nonzero value into Y. The program tests
whether this inserter, and all the member functions of class ostream, exhibit
at least some of their required behavior.
If all goes well, the program prints:
SUCCESS testing <ostre~
Exercises
Exercise 9.1 Write the manipulator general that sets the format flags in floatfield to
specify adaptive floating-point output (as for the g conversion specifier to
printf).
Exercise 9.2 List all manipulators, not currently declared in <ios> or <ostream>, that
you think might set the format flags in useful ways.
Exercise 9.3 Besides flushing the output stream, what kind of operations do you think
ostream: : osfx () might have to perfonn?
246 Chapter 9
Figure 9. 14: II test <ostream>
#inc1ude <assert.h>
tostream.c
#inc1ude <string.h>
Part 1 of 2 #inc1ude <ostream>
#inc1ude <iostream>
#inc1ude <strstream>
II class Boolean
class Boolean (
public:
Boo1ean(int v)
: val (v) {}
int va1ue() const
(return (val);
int va1ue(int v)
{return (val = v);
private:
int val;
};
int maine)
( II test basic workings of ostream definitions
static const char output[] = IIs1s2**s3@-s4??s5 bc\n ll
II 1 23 -456 +7890 3 12 ??010 Ox??3e OXAB O\n ll
II +12 14.0000 -?1.68E-OS 1.000 2.000E+00\n ll
II N Y\n ll
IIrest of stream\n ll
II sl s2 s3 11 ;
ostrstream ostrstr;
ostream outs(ostrstr.rdbuf(»;
assert (outs.good() && (outs.flags() & ios::showbase) 0);
outs « noskipws;
assert«outs.flags() « (ios:: dec ios::skipws»
ios: :dec);
II test character inserters
outs « "sl l1 ;
outs.fi11('*'), outs.width(4), outs « 1182 11 ;
outs.fi1l('@'), outs.width(3), outs « left « IIs3";
outs.fil1(''''''), outs.width(3), outs « right « IIs4 11 ;
outs.fi1l('?'), outs.width(4), outs « internal « "sS";
outs « I I « (unsigned char)'b' « (signed char) 'e'
« endl « flush;
II test integer inserters
<ostream> 247
Continuing outs « ' , « (short)l « ' , « (unsigned short)23;
outs « ' , « (int) -456 « ' ';
tostream.c outs « showpos « (int)7890
Part 2 of 2 « ' , « (unsigned int)3 « noshowpos;
outs « ' , « oct « (long)10;
outs.width(S), outs « ' , « showbase « (long) 8;
outs.width(6), outs « ' , « hex « (unsigned long)Ox3e;
outs « ' , « uppercase « (unsigned long)Oxab;
outs « ' , « (unsigned long)O « endl;
II test floating-point inserters
outs « ' , « showpos « (float)12 « noshowpos;
outs « ' , « showpoint « (double)14 « noshowpoint;
outs.precision(3), outs.width(10);
outs « ' , « (long double)-168e-7;
outs « fixed « ' , « 1.0;
outs « scientific « ' , « 2.0 « endl;
II test Boolean inserter
const Boolean no(O), yes(l);
assert(no.value() == 0 && yes.value() 1);
outs « ' , « no « ' , « yes « endl;
II test streambuf inserter and puts
istrstream istr("rest of stream\n");
outs « *istr.rdbuf();
out s •put (' '). wri te ( II S 1", 2);
outs.write«unsigned char *)11 s2 ", 3);
outs.write«signed char *)" s3", 3);
outs.flush() « ends « (void *)&output;
assert(strcmp(ostrstr.str(), output) == 0);
cout « "SUCCESS testing <ostream>" « endl;
return (0);
o
Background
The header <iomanip> presents several templates and a handful of
manipulators that exploit them. I have already presented these manipula-
tors, alongside the ios member functions that perform similar operations.
(See the discussion of formatting information, beginning on page 124.)
This is the first of several headers that define templates. (See also
Chapter 17: <bit s>, Chapter 19: <dynarray>, and Chapter 20: <ptrdynar-
ray>.) It is also arguably the trickiest use of templates in the Standard C++
libraI)T, at least for now. I begin, therefore, by sketching the history that led
to the templates in <iomanip>. I also include here a general discussion of
templates, current template technolog}', and how I implement templates in
this book.
manipulators Manipulators have lots of uses. The ones I've shown so far let you alter
the formatting information stored in an object based on class ios, extract
white space, insert special characters, and flush output streams. And they
let you do so in the guise of inserting or extracting a function designator
with a reasonably mnemonic name. Perhaps the happiest example of all is:
cout « end!;
which inserts a newline character to end the current text line and flushes
the output to any waiting interactive display.
manipulator For all their power, however, these inserters share a common limitation.
limitations You have no way to convey any additional, variable information. The name
of the function says what it does, but that's also all it says. The limitation is
intrinsic in the operator notation:
ostream& operator«(ostream& os, x X)i
It is clear that you can pass only one additional argument to the left shift
(insertion) operator, besides the necessary ostream&: left argument. For a
manipulator, that additional argument x of type x must convey the address
of the function to call, as in (*x) (os). (x is presumably some pointer to
function type, defined earlier in the translation unit.) There's hardly any
room to convey additional arguments.
Or is there? Remember, x can be an arbitrary class. You can construct a
class that stores both a function pointer and an extra argument, as in:
250 Chapter 10
class X {
public:
X(ostream& (*pf_arg)(ostream&, T), T val_arg)
: pf(pf_arg), val (val_arg) {}
friend ostream& operator«(ostream&, X);
private:
ostream& (*pf)(ostrea&&, T);
T val;
};
Presumabl)T, T is defined as a type earlier in the translation unit. You can
then overload the insertion operator, as in:
ostream& operator«(ostream& os, X x)
{ II assemble function call and do it
return «(*x.pf) (os, x.val»;
}
This inserter accepts an object of class X, digs out the pieces, and makes the
desired function call. (It is a friend of the class so it can see the pieces.)
To use this machinery, you have to supply a pair of functions:
• the visible function, taking a single argument of type T, that you call to
obtain the right operand of an insertion operator
• the hidden function, taking two arguments, that does the actual work
The visible function assembles an object of class x. Inserting that object
causes the hidden function to get called.
example Here's a realistic example. Say you want to insert an occasional integer
in hexadecimal format, but you don't want to have to keep fiddling the
format bits. After each such insertion, you want the integer base to revert
to whatever it was before the hexadecimal insertion. To do so, you can write
the function:
ostream& insert~ex(ostream& os, int val)
{ II insert a hex integer
ios::fmtflags old = os.setf(ios::hex, ios::basefield);
os « val;
os.setf(old, ios::basefield);
return (os);
}
You can then write calls such as insert_hex(cout, mask). But that
doesn't play so nicely with the inserter notation. Better to make this the
hidden function, using the machinery I showed above. The visible function
might then overload the name hex, as in:
X hex(int val)
{ II save info to call insert_hex (val)
return (X (&insert_hex, val»;
}
Inserting an X<int> does indeed instantiate the int version of this function.
template Such powerful notation is a two-edged sword. The translator is obliged
limitations to guess, from the argument types, what corresponding template parame-
ters you must have intended. You are thus obliged to help the translator in
some ways:
• Every template function parameter must affect the type of one or more
function parameters in some wa)T, however else it is used in the function
definition.
• Value parameters cannot be used as template function parameters, since
they seldom can influence overload resolution.
<iomanip> 253
The Committee has eliminated some of these constraints, but current
implementations still have them. You can also find differences of interpre-
tation among implementations - no surprise with any fairly new technol-
og)'- So the bottom line is, don't ask too much of the machinery for
automatically instantiating template functions.
implementation Templates have other limitations as well, at least for now. Current
restrictions implementations have fundamental differences in how they manage tem-
plate definitions. You can, in principle, write a template class like any other.
You define the template class itself in a header, but keep the member
function definitions in a separate source file or files. In practice, however,
not all implementations deal with this approach well, if at all. And some
implementations have limited capacity for dealing with complex template
definitions.
Writing inline definitions for template functions of all flavors seems to
work most reliably in current implementations. Of course, you run risks in
doing so, at least with certain compilers. A nontrivial inline function
definition may still expand to inline code, so the compiler generates an
excess of code. Other compilers choose not to inline code that contains
loops, such as for or while statements. (The cfront preprocessor won't even
translate such code. It favors a different template style.) Whether or not that
is a good decision, the compilers still see fit to complain repeatedly about
such code. I have had reasonable programs fail to compile simply because
the compiler decided it had emitted too many (gratuitous) diagnostics.
template The style I chose for this book is to write only inline definitions for all
style template functions. I try to keep them small, for a host of reasons. Occa-
sionall}', I offload some work to secret functions that are shared across
template instantiations. But the basic risks of this approach remain, as I
outlined above. Still, I find this approach best for the sake of portability and
clear presentation today: You may need to alter the representation of some
templates presented here, if for no other reason than to improve translation
speed, execution speed, or generated code size.
The template class imanip<T> describes an object that can store a function
pointer and an object of type T. The designated function accepts an argument of
this type T. For the sake of exposition, the maintained data is presented here as:
• istream&: (*pf) (istream&, T), the function pointer;
• T manarg, the object of type T.
<iomanip> 255
17.4.5.2.1 imanip<T>: :imanip(istream&: (*) (istream&:, T), T}
17.4.5.4.3 setbase(int)
setbase smanip<int> setbase(int base};
Returns smanip<int> (&of, base), where f can be defined as:
ios& £(ios& str, int base)
( II set basefield
str.setf(n == 8 ? ios::oct : n == 10 ? ios::dec
: n == 16 ? ios::hex : (ios::fmtflags}O, ios::basefield);
return (str);
}
17.4.5.4.4 setfill(int)
setfill smanip<int> setfill(int e);
Returns smanip<int> (&of, c), where f can be defined as:
ios& £(ios& str, int e)
( II set fill character
str.fill(c);
return (str);
17.4.5.4.5 setprecision(int)
setprecision smanip<int> setprecision(int n);
Returns smanip<int> (&of, n), where f can be defined as:
ios& £(ios& str, int n)
( II set precision
str.precision(n);
return (str);
}
17.4.5.4.6 setw(int)
setw smanip<int> setw(int n);
Returns smanip<int> (&of, n), where fcan be defined as:
ios& £(ios& str, int n)
{ II set width
str.width(n) ;
return (str);
Footnotes:
Footnotes 105) The expression cin >> reset iosf lags (ios : : skipws ) clears
ios: : skipws in the format flags stored in the istream object cin (the
same as cin » noskipws), and the expression cout « resetios-
flags (ios : : showbase ) clears ios:: showbase in the format flags
stored in the ostream object cout (the same as cout « noshowbase).
<iomanip> 257
Future Directions
wide-character The template classes defined in <iomanip> are also altered by the
streams addition of wide-character streams to the Standard C++ librar)'. (See page
119.) Additional template parameters specify the kind of stream.
Using <iomanip>
You include the header <iomanip> for one of two reasons:
• to use one or more of the predefined manipulators that take an argument
• to define one or more additional manipulators that take an argument
predefined I showed how to use the predefined manipulators much earlier, in
manipulators conjunction with the ios member functions that access and alter formatting
information. (See page 124.) All are based on the template class smanip<T>,
to work with both extractors and inserters. Very briefly:
resetiosf1ags • resetiosf1ags (fmtf1ags), clears the specified format flags
setios flags • set iosf lags (fmt flags) , sets the specified format flags
sethase • sethase (int), sets the hasef ie1d format flags to match the specified
numeric base
setf ill • set fill ( int ) , alters the fill character
setprecision. setprecision (int), alters the floating-point precision
setw. setw( int), alters the field width
The manipulator notation usually results in more code generated and
executed than for direct calls to the ios member functions. But unless you
can demonstrate, in a particular case, that the cost is important, you should
probably use manipulators wherever possible, if only for clarit)T.
user-defined You can define your own manipulators as well, using the templates
manipulators defined in <iomanip>. A few guidelines:
• Use these templates only when you have variable information to convey.
Manipulators that take no arguments are much easier to write. (See, for
example, the predefined manipulators in Chapter 6: <ios>, Chapter 8:
<istream>, and Chapter 9: <ostream>.)
• Some objects don't suffer copying about. (Objects of class streambuf are
an obvious example.) For these, pass a pointer instead, if the object does
not always exist. Otherwise, pass a reference to the object.
• If you need to pass more than one value, define a class that holds all the
needed values. Call the manipulator with a constructor for the compos-
ite value as its single argument value.
• Use class smanip<T> only for a manipulator that makes sense as both an
inserter and an extractor, and only when the ios subobject is all you need
to manipulate. Otherwise, use class imanip<T> or class omanip<T>.
As always, I encourage you to find an existing manipulator that does
something similar to what you want to do, then imitate its structure.
258 Chapter 10
Figure 10. 1: II iomanip standard header
#ifndef _~OMANXP_
iomanip #define _XOMANXP_
Part 1 of 2 #include <istream>
#include <ostream>
II template class smanip
template<class _T> class smanip {
public:
smanip(ios& (*_F) (ios&, _T), T _A)
: _Pf(_F), _Manarg(_A) {}
ios& (*_Pf) (ios&, _T);
_T _Manarg;
};
template<class _T> inline
istream& operator»(istream& _X, const smanip<_T>& _M)
{ II apply manipulator to input stream
_TRY_BEGIN
(*_Me_pf) (_X, _Me_Manarg);
_CATCH_ALL
_Xesetstate(ios::failbit);
_CATCH_END
return (_X);
}
template<class _T> inline
ostream& operator«(ostream& _0, const smanip<_T>& _M)
{ II apply manipulator to output stream
_TRY_BEGIN
(*_Me_pf) (_0, _Me_Manarg);
_CATCH_ALL
_Oesetstate(ios::failbit);
_CATCH_END
return (_0);
}
II template class imanip
template<class _T> class imanip {
public:
imanip(istream& (*_F) (istream&, _T), _T _A)
: _Pf(_F), _Manarg(_A) {}
istream& (*_Pf) (istream&, _T);
_T _Manarg;
};
template<class _T> inline
istream& operator»(istream& _X, const imanip<_T>& _M)
{ II apply input manipulator to input stream
_TRY_BEGIN
(*_Me_pf) (_X, _Me_Manarg);
_CATCH_ALL
_Xesetstate(ios::failbit);
_CATCH_END
return (_X);
}
II template class omanip
template<class _T> class omanip {
public:
omanip (ostream&: (*_F) (ostream&:, _T), _T _A)
<iomanip> 259
Continuing : _Pf(_F), _Manarg(_A) {}
ostream& (*_Pf)(ostream&, _T);
iomanip _T _Manarg;
Part 2 of 2 };
template<class _T> inline
ostream& operator«(ostream& _0, const omanip<_T>& _M)
{ II apply manipulator to output stream
_TRY_BEGIN
(*_M._Pf) (_0, _M._Manarg);
_CATCH_ALL
_O.setstate(ios::failbit);
_CATCH_END
return (_0);
}
II instantiations
smanip<ios::fmtflags> resetiosflags(ios::fmtflags);
smanip<ios::fmtflags> setiosflags(ios::fmtflags);
smanip<int> setbase(int);
smanip<int> setfill(int);
smanip<int> setprecision(int);
smanip<int> setw(int);
#endif o
smanip<int> setbase(int n)
II extractor/inserter to set base field
return (smanip<int>(&sbfun, n»;
o
260 Chapter 10
Figure 10.4: II setfill -- setfill(int)
#include <iomanip>
setfill.c
static ios& sffun(ios& iostr, int ch)
( II set fill character
iostr.fill(ch);
return (iostr);
Implementing <iomanip>
iomanip Figure 10.1 shows the file iomanip, which implements the standard
header <iomanip>. All template functions appear within the header as
inline definitions, as I discussed earlier in this chapter. (See page 253.) Note
that the template classes have public (but secret) member objects, so the
template functions need not be declared as friend functions.
_TRY_:IO_BEG:IN For a description of the macros _TRY_:IO_BEGIN and _CATCH_IO_END, see
_CATCB_:IO_END page 128. As usual, they expand to the code needed to handle exceptions
for inserters and extractors. The implementation of these template classes
and functions is otherwise as I outlined early in this chapter.
resetios.c The remaining code implements the predefined manipulators. All fol-
setbase.c low the same pattern, which I also outlined earlier. Figure 10.2 shows the
setfill.c file resetios .c, which defines the manipulator resetiosflags. Figure
setiosfl.c 10.3 shows the file setbase.c, which defines the manipulator setbase.
setpreci.c Figure 10.4 shows the file setfi11. c, which defines the manipulator
setw.c setfill. Figure 10.5 shows the file setiosfl.c, which defines the ma-
nipulator setiosflags. Figure 10.6 shows the file setpreci. c, which
defines the manipulator setprecision. And Figure 10.7 shows the file
setw. c, which defines the manipulator setw. Each of these files also defines
(locally) the hidden function that the manipulator calls.
<iomanip> 261
Figure 10.6: // setprecision -- setprecision(int)
#include <iomanip>
setpreci.c
static ios& spfun(ios& iostr, int n)
( // set precision field
iostr.precision(n);
return (iostr);
smanip<int> setprecision(int n)
( // extractor/inserter to set precision field
return (smanip<int>(&spfun, n»;
o
smanip<int> setw(int n)
( // extractor/inserter to set width field
return (smanip<int>(&swfun, n»;
o
Testing <iomanip>
tiomanip. c Figure 10.8 shows the file tiomanip .c. It tests the basic properties of the
template classes defined in <iomanip> by instantiating each template class
and calling the associated manipulator. The three manipulators defined
here are even arguably useful. The program also tests that the predefined
manipulators do more or less what they should.
If all goes well, the program prints:
SUCCESS testing <iomanip>
and takes a normal exit.
Exercises
Exercise 10.1 Write the header <iomanip2> which defines two-parameter versions of all
the template classes in <iomanip>. The two parameters should have inde-
pendent types. Why would you want such a capability?
Exercise 10.2 Write a set of macros that behave essentially the same as the template
classes defined in <iomanip>. Are there any circumstances where you
might prefer the macros over the templates?
262 Chapter 10
Figure 10.8: II test <iomanip>
#include <assert.h>
tiomanip.c #include <iomanip>
Part 1 of 2 #include <iostream>
#include <strstream>
int main ( )
( II test basic workings of iomanip definitions
cin.flags(O);
assert(cin.flags() ==(ios::fmtflags)O);
cin » setiosflags(ios::left I ios::showpoint);
assert(cin.flags() ==(ios::left I ios::showpoint»;
<iomanip> 263
Exercise 10.3 Write the manipulator outfmt (const char *), based on omanip<T>, that
alters formatting information to match the fprintf-style conversion speci-
fication in its format string argument. Thus, cout « outfmt ("X= %0 11
)
<< n should insert the string II x= ", then insert the octal integer value n.
Exercise 10.4 Write the manipulator infmt(const char *), based on imanip<T>, that
alters formatting information to match the fscanf-style conversion speci-
fication in its format string argument. Thus, cin » infmt ("x= 9'00") »
n should extract and match the string IIX=", skip white space, then extract
an octal integer into n.
Exercise 10.5 [Harder] Alter the manipulators you wrote for the previous two exercises
so that the format string can contain multiple conversion specifications. The
manipulator next advances to the next conversion specification, as in:
cout « outfmt (II (9'og, %5. 2f) \n ll
« re
)
« next « im « next;
Exercise 10.6 [Very hard] Alter the result of the previous exercise to eliminate the need
for the manipulator next.
Chapter 11: <strstream>
Background
The header <strstream> defines three classes that cooperate to help you
read and write character sequences stored in memory:
strstreambuf - strstreambuf, derived from streambuf to mediate access to an in-
memory cllaracter sequence and grow it on demand
istrstream - istrstream, derived from istream to construct a strstreambuf object
with an input stream and to assist in extracting from the stream
ostrstream - ostrstream, derived from ostream to construct a strstreambuf object
with both input and output streams and to assist in inserting into the
stream
An in-memory character sequence is, in many ways, the simplest kind of
stream buffer. The buffer maintained by the controlling streambuf object
is not merely a window on some separate representation, like an external
file. Rather, the buffer is the character sequence in its entiret)r.
uses Nevertheless, a strstreambuf object can control a variety of in-memory
character sequences:
- an array of characters all of whose elements are defined from the outset
- an array of characters whose initial elements contain a null-terminated
string
- an initially empty array of characters that can grow on demand, with
storage allocated by new expressions and freed by delete expressions
- an initially empty array of characters that can grow on demand, with
storage allocated and freed by functions supplied by the program
All these choices are reflected in a plethora of constructors for the three
classes defined in <strstream>.
Despite all the options, three simple choices stand out. You can initialize
an istrstreamobject to control a constant array of characters. You can then
extract from its associated input stream, but you cannot insert to the output
stream. The result is effectively an istream object where you dictate the
stream contents purely within the program. Or you can initialize an
ostrstream object to control a non-constant array of characters. You can
then insert into its associated output stream to store into the array:
266 Chapter 11
Your third simple choice is to initialize an ostrstream object to control
an initially empty dynamic output stream that can grow on demand. The
result is effectively an ostream object where you capture the stream con-
tents purely within the program. You can then capture the final stream
contents before the ostrstream object is destroyed. Class strstreambuf
supplies several member functions to help you capture these contents. For
an object x of class strstreambuf, here's what you can do:
str - Call x. str() to obtain a pointer to the start of the controlled character
sequence. The function freezes the character sequence to prevent further
insertions. Freezing also prevents the destructor for x from freeing the
character sequence - you assume that responsibility when you obtain
the pointer.
freeze - Call x.freeze() to freeze the character sequence, or x.freeze(O) to
unfreeze it. The latter call is particularly helpful if you've learned what
you need from an earlier x. str () call and now want the destructor to
free the character sequence for you.
pcount - Call x.pcount () to count the number of characters inserted in the
sequence. The count is useful information for arbitrary character values,
or if you don't insert a null character at the end of the character sequence.
ends Here is the obvious use for the manipulator ends, by the way. (See page
223.) It's a clear way to supply a null character at the end of an in-memory
character sequence.
sscanf I end this briefintroduction by mentioning the ghost at the banquet table.
sprintf The Standard C library has long supported a similar capabilit~ You can
vsprintf obtain formatted input from an in-memory character sequence by calling
sscanf. You can write formatted output to an in-memory character se-
quence by calling sprintf or vsprintf. (All three functions are declared
in <stdio.h>.) So how are these three classes any better? The answer is
easy:
- An istrstream object looks like any other istream object controlling
an input stream. And an ostrstream object looks like any other ostream
object controlling an output stream. The older functions oblige you to
know that you're dealing with an in-memory character sequence. And
you a;ways have to know exactly where the sequence resides.
- Unlike sscanf, an istrstream object can control a sequence of arbitrary
character values. It can have embedded null characters. Equally, it
doesn't need a terminating null character.
- sprintf writes to an array of fixed length whose length is nevertheless
not known to the function. You avoid storage overwrites only by restrict-
ing every single conversion specification. By contrast, an ostrstream
object can control a sequence of known length. Insertions fail before
storage overwrite occurs. Or the object can control a dynamic sequence
of arbitrary length. Insertions fail only when heap storage is exhausted.
I believe these are ample reasons to cultivate a knowledge of the header
<strstream>.
<strstream> 267
Footnotes:
Footnotes 106) The return value can be a null pointer.
107) An implementation should consider alsize in making this decision.
Future Directions
str The member function name str has already been changed in class
string since the Informal Review Draft. As part of a general homogeniza-
tion of names across classes, it is now data. That cleanup has yet to extend
to the classes defined in <strstream>, but it might.
wide-character As I keep mentioning where relevant, a major change is the addition of
streams wide-character streams. (See page 119.) Class strstreambuf becomes a
template class parameterized by the type of the stream element. One
instantiation, for type char, has essentially the same functionality as de-
scribed in this chapter. Another, for type wchar_t, supports streams of
elements from some large character set. Classes istrstream and ostr-
stream change along similar lines.
Using <strstream>
You include the header <strstream> to make use of any of the classes
istrstream, ostrstream, or strstreambuf. Objects of these classes let
you read and write in-memory character sequences just as if they were
conventional files. You can choose among four patterns of access:
• read only
• write only
• simple read/write
• sophisticated read/write
I deal with each of these options in tum.
read only If all you want to do is read an in-memory character sequence, construct
an object of class istrstream to specify the character sequence and control
extractions from it. For a null-terminated string s, you can write:
istrstream strin(s);
The character sequence begins at s and continues up to, but not including,
the terminating null character that follows.
To control an array of n characters s with arbitrary values, you can write:
istrstream strin(s, n);
276 Chapter 11
In either case, s can be either a pointer to char or a pointer to const char.
Whichever way you construct the istrstream object, the resultant stream
buffer (pointed at by strin. rdbuf ( )} does not support insertions. You
must specify an initial character sequence - class istrstream has no
default constructor. The character sequence remains unchanged for the life
of the istrstream object. (I suggest you refrain from altering the contents
of the character sequence by other means, if that is possible, while the
istrstream object controls accesses to it.)
stream Positioning within a read-only stream is fairly simple. A streamoff
positioning value is effectively an index into the character sequence. Thus:
strin.rdbuf()->pubseekoff(O, ios::beg);
sets the stream position at the beginning (position zero) of the character
sequence. If the length of the sequence is nonzero, the next character
extracted will be the first character in the sequence. Any attempt to set the
stream position to a negative value, or beyond the end of the character
sequence, will fail. You can also call streambuf: :pubseekpos, as above,
but that is less necessary for an in-memory character sequence. Do so for
code that shouldn't need to know what flavor stream buffer it is really
dealing with. (For a general discussion of stream positioning, see page 160.)
str For what it's worth, you can also call strin.str() for a read-only
character sequence. The call does not freeze the character sequence, since it
is not dynamically alterable. The function merely returns a pointer to the
beginning of the character sequence, just as for a character sequence you
construct (as described below). This is always the beginning of the character
sequence you specified when constructing the object.
pcount If you do call strin. str () for a read-only character sequence as above,
be warned. The call strin.rdbuf () ->pcount () will not return the length
of the sequence. Since no output stream exists, this call returns zero. You
must determine the length of the sequence by some other means, such as
positioning the stream at the end and inspecting the resultant stream offset.
write only If all you want to do is create an in-memory character sequence, con-
struct an object of class ostrstream to control insertions into it. You can
write:
ostrstream strout;
then insert into strout just like any other output stream. The character
sequence can grow dynamically to arbitrary length. (The actual limit is
usually :tNT_MAX, defined in <limits.h>, or when a storage allocation
request fails.)
ends If you want a null-terminated string, be sure to insert a null character
pcount last, as with:
strout « ends;
It will not be supplied for you when you call strout. str ( ). For a sequence
of arbitrary characters, you can determine the number of characters you
inserted by calling strout .pcount (). (But see the warning below.)
<strstream> 277
stream Positioning within a write-only stream is also fairly simple. You can work
positioning mostly with streamoff values, as for read-only streams described above.
A few caveats are in order, however:
• Current practice seems to vary on where you can set the stream position.
The safest bet is to move only to character positions with values already
defined - either at construction or as a result of earlier insertions.
• Some confusion is also endemic about the interaction of stream position-
ing requests and the value returned by a call such as s trout •pcount ( ) .
• Even an implementation that obeys the draft C++ Standard can still
surprise. Strictly speaking, ostrstream: :pcount calls strstream-
buf: :pcount, which does not return a count of all insertions. Rather, it
returns the difference between the current output stream position and the
initial output stream position. The former can be left other than at the end
of the character sequence by stream-positioning requests. The latter can
be set beyond the beginning of the character sequence by a more sophis-
ticated strstreambuf constructor, as described below.
As usual, my advice is to avoid such delicate areas in code you hope to keep
portable. If you insist on pushing the limits of clearly defined behavior,
expect surprises.
str Your goal in creating a write-only character sequence is to capture the
final result, as a rule. Call strout. str () to freeze the character sequence
and return a pointer to its initial character. Remember that freezing the
character sequence tells the strstreambuf destructor not to free storage for
the character sequence. You must either free the storage yourself or be sure
to call strout. freeze ( O) before strout is destroyed.
simple If you want to create an in-memory character sequence that you can read
read/write as well as write, you need two objects to control the input and output
streams. First, construct an object of class ostrstream to supply the
strstreambuf object and control insertions, then construct an object of
class istream to control extractions. You can write:
ostrstream strout;
istream strin(strout.rdbuf(}};
much as I have described several times earlier for setting up a bidirectional
stream. (See, for example, page 188.) You can then insert into strout just
like any other output stream. And once you have inserted characters, you
can extract them from strin just like any other input stream.
eofbit A few caveats are in order, however. Try to extract beyond the last
character in the sequence and you will encounter end-of-file. Once you
extend the sequence by inserting characters, you can successfully extract
again, but not until you clear eofbit if it is set in the istream object. Once
again, this status bit proves less than trustworthy:
stream You can position the input and output streams separately or jointly: As
positioning usual, positioning operations demand a modicum of caution:
278 Chapter 11
• The draft C++ Standard specifies a default third arguinent value which
= ios:: in I ios: :out for streambuf: :pubseekoff. (It specifies the
same for the default second argumellt to streambuf: :pubseekpos.)
Thus, unless you supply an actual which argument that selects only Olle
stream - ios:: in or ios:: out - a call to this member function
endeavors to position both streams ill tandeIl1. Seldoln does that make
sense. When both reading and writing an ill-melnory character se-
quence, always specify the which argumellt on such calls.
• The draft C++ Standard is even nastier at times. If the second argument
to streambuf: :pubseekoff is ios: :cur, a talldem positioning opera-
tion will fail. For such relative positioning requests, you should always
specify a which argument that selects only one stream.
Note that the second caveat applies even to read-only or write-only in-
memory character sequences.
rdbuf As an important aside, here is a warning regarding the lnember function
rdbuf. Several classes in the Standard C++ library are derived from either
istream or ostream. These include istrstream alld ostrstream, the
topics of this chapter. All such derived classes also provide a member
function rdbuf () that hides the member function in the base class ios.
Why is this so? All such derived classes also provide a lnember object of
a class derived from streambuf. In this chapter, that derived class happens
to be strstreambuf. Consider, for example, the call istr. rdbuf ( ), for an
object istr of class istrstream. It returns a pointer to the strstreambuf
member object in istr. And the return type is illdeed pointer to strstream-
bUf, not pointer to streambuf as in the base class ios.
A generic pointer to the base streambuf gets you to all the inherited
member functions. It even gets you to the proper overriding definitions of
virtual member functiollS. But to access any member functions peculiar to
the derived class, you l1eed such a specialized poirlter. Again in this par-
ticular case, the member fUllctiollS freeze, pcount, and str are peculiar to
class strstreambuf, not its base class.
Potential confusion arises, however, once you Inake a call such as
istr. rdbuf (strout. rdbuf ( ) ). The critical poillter in the ios subobject
now desigtlates a differel1t strealn buffer. But a subsequent call to
istr. rdbuf () still returns a pointer to the raeraber object within istr. To get
the pointer actually used by inserters and extractors, ~you l11Ust lnake the
hairy call ( (ios&:) istr) . rdbuf ().
Got all that? If not, don't worry too much about it. The bottolTlline is
that you should follow a fundamental style rule. Alter the stored stream
buffer pointer only in an object of class istreamor ostream. Never do so in
an object of a class derived froin one of these. Evell better, do as I did above
- use a special istream or ostream object COl1structed from the outset to
share an existing stream buffer. With either approach, there's never a
second stream buffer lying around to cause confusion.
<strstream> 279
sophisticates You can get even fancier with the classes defined in <strstream>. What
read/write follows is an assortment of more sophisticated setups.
Say you want to use an existing character array to store a character
sequence, and you want to read the character sequence once you write it.
If you declare:
ostrstream strout(s, n);
istream strin(strout.rdbuf(»;
then strout controls the sequence of n characters beginning at s. Both the
read position and the write position are at the beginning of the character
sequence. In this case, you cannot extend the sequence by inserting at or
beyond character position n.
Here's a variation on this scenario. Say you want to use an existing
character array to store a character sequence, and the array already stores
a null-terminated string. You want to begin extracting from the beginning
of the cllaracter sequence, but you want to begin inserting at the end of the
null-terminated string (first replacing the terminating null). If you declare:
ostrstream strout(s, n, ios::app);
istream strin(strout.rdbuf(»;
then you get the desired behavior. Insertions effectively append characters
to an initial null-terminated string, but won't continue past the end of the
character array: For extractions, see the caveat above about the behavior of
eofbit.
If you want to get fancier, you have to construct a strstreambuf object
directly: This class has lots of constructors with lots of options. I describe
here just the two tllat I feel are genuinely useful and safe.
preallocated Say you want to construct an in-memory character sequence that you
buffers know will get very large. You'd like to suggest, when constructing the
strstreambuf object, that the object allocate space for a large sequence
right off the mark. That can save lots of execution overhead in reallocating
storage as the character sequence grows. Equall)', you might want to
construct a number of character sequences all of wInch will be fairly small.
You'd like to suggest that each such object allocate only a small number of
characters. That can save lots of unused storage. If you declare:
strstreambuf sb(n);
istream strin(&sb);
ostream strout(&sb);
for some int value n, the constructor should take the hint. What it does with
it is up to the implementation, but at least you get to make the suggestion.
user-defined Another approach to storage allocation is to do the job yourself. So your
allocation final interesting choice is to begin with two functions like:
#include <stdlib.h>
void *my_alloc(size_t n)
{ II allocate n chars
return (malloc(n»;
}
280 Chapter 11
void my_free(void *p)
{ 1/ free allocated storage
free (p) ;
}
Tailor these basic functions as you see fit. Then you can declare:
strstreambuf sb(&my_alloc, ~y_free);
istream strin(&sb);
ostream strout(&sb);
The two functions you supply will be called, in place of new and delete
expressions, to allocate and free storage for character sequences.
strstreambuf::-strstrearnbuf()
{ II destruct a strstreambuf
_Tidy ( );
}
else
{ II make static
int size = n < 0 ? INT_MAX n o ? strlen(gp) : n;
_.Alsize = 0;
_Seekhigh = gp + size;
if (pp == 0)
setg(gp, gp, gp + size);
else
{ II make writable too
if (pp < gp)
<strstream> 283
Continuing pp = gp;
else if (gp + size < pp)
strstrea.c
pp = gp + size;
Part 2 of 2 setp(pp, gp + size);
setg(gp, gp, pp);
}
void strstreambuf::_Tidy()
{ II discard any allocated storage
if «_St~ode & (_Allocated I _Frozen» != _Allocated)
else if (_Pfree != 0)
(*_Pfree) (eback(»;
else
delete [] eback();
_Seekhigh = 0;
_Strmode &= -(_Allocated _Frozen);
} o
Implementing <strstream>
strstrea Figure 11.1 shows the file strstrea, which implements the standard
header <strstream>. It defines the classes strstreambuf, istrstream,
and ostrstream. For a description of the macro _BiTMASK, see page 132.
For a description of the macro _HAS_SiGNED_CHAR, see page 195.
I have added two macros to specify in one place two values that are
judgement calls. Both deal with the number of characters to allocate when
creating or extending a character seqeunce:
_ALSIZE - _ALSiZE, the initial number of bytes to allocate, absent any hints to the
contrary (currently 512)
_MiNSiZE - _MiNSiZE, the minimum number of additional bytes to allocate when
extending (currently 32)
You may well have reasons to alter either or both of these values, based on
what you know about storage size and granularity on a given implemen-
tation.
_Strstate The bitmask type _Strstate, defined within the class strstreambuf,
has milch the same meaning as its counterpart strstate in the draft c++
Standard (17.4.6.1). I have added the element _Roread, which is not used
by the classes defined in <strstream>. Adding it here, however, greatly
simplifies the implementation of the classes defined in <sstream>. (See the
next chapter.) The meaning of each of the _Strstate elements is:
_Allocated - _Allocated, set when the character sequence has been allocated
_Constant - _Constant, set when the character sequence is not to permit insertions
_Dynamic • _Dynamic, set when the character sequence can grow on demand
_Frozen - _Frozen, set when the character sequence has been frozen
_Roread - _Noread, set when the character sequence is not to permit extractions
284 Chapter 11
Figure 11.3: II strstpro -- strstreambuf protected members
#include <string.h>
strstpro.c
#include <strstream>
Part 1 of 2
int strstreambuf::overflow(int ch)
{ II try to extend write area
if (pptr() 1= 0 && pptr() < epptr(»
return (*_Pn()++ = ch);
else if (1(_St~ode & _Dynamic)
II _Strmode & (_Constant 1 _Frozen»
return (EOP);
else
{ II okay to extend
int osize = gptr() == 0 ? 0 : epptr() - eback();
int nsize = osize + _Alsize;
char *p = Palloc 1= 0 ? (char *)(*_Palloc) (nsize)
: new char[nsize];
if (p == 0)
return (EOP);
if (0 < osize)
memcpy(p, eback(), osize);
else if (_ALSXZE < _Alsize)
_Alsize = _ALSXZE;
if (I (_Strmode & _Allocated»
else if (_Pfree 1= 0)
(*_Pfree) (eback(»;
else
delete [] eback();
_Strmode 1= _Allocated;
if (osize == 0)
{ II setup new buffer
_Seekhigh = p;
setp(p, p + nsize};
setg(p, p, p);
}
else
{ II revise old pointers
_Seekhigh = _Seekhigh - eback() + p;
setp(pbase() - eback() + p, pptr() - eback() + p,
p + nsize);
if (_St~ode & _Roread)
setg(p, p, p);
else
setg(p, gptr() - eback() + p, pptr() + 1);
int strstreamhuf::underflow()
( II read only if read position availahle
if (gptr() == 0)
return (EOF);
else if (gptr() < egptr(»
return (*_Gn(»;
else if (_St~ode & _Noread I I pptr() == 0
I I pptr() <= gptr() && _Seekhigh <= gptr(»
return (EOF);
else
{ II update _Seekhigh and expand read region
if (_Seekhigh < pptr(»
_Seekhigh = pptr();
setg(ehack(), gptr(), _Seekhigh);
return (*_Gn(»;
else
off = _BADOFF;
}
else if (which & ios::out && pptr() 1= 0)
{ II set only output pointer
if (way == ios::end)
off += _Seekhigh - eback();
else if (way == ios::cur)
off += pptr() - eback();
else if (way 1= ios::beg II off _BADOFF)
off = _BADOFF;
if (0 <= off && off <= _Seekhigh - eback(»
pbump(eback() - pptr() + off);
else
<strstream> 287
Continuing off = _BADOFF;
strstpos.c
else II nothing to set
Part 2 of 2 off =
_BADOFF;
return (streampos(off»;
else
off = _BADOFF;
}
else if (which & ios::out && pptr() 1= 0)
{ II set output pointer
if (0 <= off && off <= _Seekhigh - eback(»
pbump(eback() - pptr() + off);
else
off = _BADOFF;
ostrstream::-ostrstream()
{ II destruct an ostrstream
} o
enforces delicate streambuf semantics that are hard to spell out in detail.
Tinker cautiously:
strstpro.c Figure 11.3 shows the file strstpro.c. It defines three functions that
override streambuf virtual member functions to insert and extract charac-
ters - overflow, pbackfail, and underflow. The inherited definition of
uflow is adequate, so no override occurs here. (See the general discussion
on page 162.) Once again, two of the three functions are straightforward.
Only overflow demands closer study.
overflow It is the business of overflow to "make a write position available," then
insert the argument character into it. If the write position is already avail-
able, or if none can be made available, the function has an easy job of it. The
hard part comes when the function must extend, or initially create, storage
for the character sequence. It must then determine the size of any existing
sequence (osize) and the desired new size (nsize). Then it can try to
allocate the new storage, copy over any existing sequence, and free an
existing sequence that was also allocated. Finally, it must determine new
settings for the streambuf pointers, using some very finicky arithmetic.
strstfre.c Figure 11.4 shows the file strstfre .c, which defines the member func-
tion strstreambuf: : freeze. Here is where the addition of the member
object strstreambuf: :_Pendsave saves the da)'. A frozen buffer must not
permit insertions, but that is not an easy thing to prevent. The streambuf
public member functions won't look past the pointers themselves if they
indicate that a write position is available. So the trick is to make the output
stream appear empty for a frozen stream buffer by jiggeringthe end pointer.
_Pendsave stores the proper value for later restoration, should the stream
buffer be unfrozen.
<strstream> 289
strstpos.c Figure 11.5 shows the file strstpos.c. It defines the two functions that
override streambuf virtual member functions to alter the stream position
- seekoff and seekpos. The often critical value in both functions is the
member object strstrambuf:: _Seekhigh. It is updated as needed to
reflect the current "end," or high-water mark, of the character sequence.
That value determines offsets relative to the end (way equals ios: : end), as
well as an upper bound for valid stream offsets. The logic of both functions
is otherwise simple but tedious.
And that concludes the source code for class strstreambuf.
istrstre. c Figure 11.6 shows the file istrstre. c. It defines the destructor for class
istrstream, which is the only member function not defined inline within
the class.
ostrstre.c Figure 11.7 shows the file ostrstre.c. It defines the destructor, and a
moderately messy constructor, for class ostrstream. I put the constructor
here mostly to hide the call to the function strlen, declared in <string. h>.
It is not permissible to include the C header that declares it in <strstream>
and I didn't want to make up a version of the function with a secret name.
Again this is the only source code for member functions of class
ostrstream not defined inline within the class.
Testing <strstream>
tstrstre. c Figure 11.8 shows the file tstrstre. c. It tests the basic properties of the
classes defined in <strstream>. It does so in four groups:
• strstreambuf objects for dynamic character sequences (tl (»
• strstreambuf objects for read-only character sequences (t2 (»
• strstreambuf objects for read-write character sequences (t3 ( »
• istrstream and ostrstream objects (main () )
If all goes well, the program prints:
SUCCESS testing <strstream>
and takes a normal exit.
Exercises
Exercise 11. 1 Describe the effect of the following code sequence:
char s[50];
ostrstream(s, sizeof (s» < "Total = II < total < ends;
Exercise 11.2 A UNIX-style pipeline is a data stream that one process writes and another
reads. If the writer gets too far ahead of the reader, it is made to wait. If the
reader catches up with the writer, it is made to wait. In what ways is a
strstreambuf object like a pipeline? In what ways does it differ?
Exercise 11.3 Describe alternate rules for checking arguments to strstreambuf : : seek-
off and strstreambuf: : seekpos to make them less surprising.
290 Chapter 11
Figure 11.8: II test <strstream>
linc1ude <assert.h>
tstrstre.c
linc1ude <limits.h>
Part 1 of 2 linc1ude <stdlib.h>
linc1ude <string.h>
linc1ude <iostream>
linc1ude <strstream>
void *sal10c(size_t n)
( II allocate storage for string
++al10cs;
return (ma110c(n»;
void tl()
( II test dynamic strstreambufs
strstreambuf sbO, sbl(l), sb2(&sa110c, &sfree);
ostream outs(&sbO);
outs « "dynamic strstreambuf 0" « ends;
assert (strcmp (sbO • str ( ), IIdynamic strstreambuf 0 tl ) 0) ;
sbO.freeze();
outs.rdbuf(&sbl);
outs « IIdynamic strstreambuf 1 11 « ends;
assert(strcmp(sbl.str(), "dynamic strstreambuf 1 11 ) 0);
sbl.freeze(l);
outs.rdbuf(&sb2);
outs « lIa110cating strstreambuf" « ends;
assert(strcmp(sb2.str(), lIa110cating strstreambuf ll ) 0);
sb2.freeze(0);
}
void t2()
( II test read-only strstreambufs
strstreambuf sbl«const char*)buf, 5);
strstreambuf sb2«unsigned char *)buf, 0);
strstreambuf sb3«signed char *)buf, -1);
strstreambuf sb4«const char *)buf, 5);
strstreambuf sb5«const unsigned char *)buf, 0);
strstreambuf sb6«const signed char *)buf, -1);
assert(strcmp(sbl.str(), buf) ==0 && sbl.pcount() == 0);
assert (sbl.pubseekoff(O, ios::end, ios::in).offset() == 5);
assert (sbl.pubseekoff(O, ios::cur, ios::out).offset() == -1);
assert(strcmp(sb2.str(), buf) == 0 && sb2.pcount() == 0);
assert(sb2.pubseekoff(0, ios::end, ios::in).offset() == 8);
assert(sb2.pubseekoff(0, ios::cur).offset() ==
-1);
<strstream> 291
Continuing assert(strcmp(sb3.str(), buf) == 0 && sb3.pcount() == 0);
assert (sb3.pubseekoff(0, ios::end, ios::in).offset()
tstrstre.c
== 1NT_MAX);
Part 2 of 2 assert(strcmp(sb4.str(), buf) == 0 && sb4.pcount() == 0);
assert (sb4.pubseekoff(0, ios::end, ios::in).offset() == 5);
assert(strcmp(sb5.str(), buf) 0 && sb5.pcount() 0);
assert(strcmp(sb6.str(), buf) == 0 && sb6.pcount() == 0);
}
void t3()
{ II test read-write strstreambufs
strstreambuf sbl(buf, 5, buf);
strstreambuf sb2«unsigned char *)buf, 0,
(unsigned char *)buf + 1);
strstreambuf sb3«signed char *)buf, -1, (signed char *)buf);
ostream outs(&sbl);
outs « 'A';
assert (strcmp(sbl. str(), "A234567S") 0
&& sbl.pcount() == 1);
outs « '8';
assert(strcmp(sbl.str(), IIA234567S II ) == 0
&& sbl.pcount() == 1);
assert (sbl.pubseekoff(6, ios::beg, ios::out).offset() -1);
assert(strcmp(sb2.str(), "A234567S") == 0
&& sb2.pcount() == 0);
outs.rdbuf(&sb2), sb2.freeze(0), outs « 'C';
assert(strcmp(sb2.str(), IIAC34567S II ) == 0);
assert (sb2.pubseekoff (6, ios::beg, ios::out).offset() 6);
assert (sb2.pcount () == 5);
assert(strcmp(sb3.str(), "AC34567S") == 0
&& sb3.pcount() == 0);
assert (sb3.pubseekoff(50, ios::cur, ios::out).offset()
== 50);
assert (sb3.pcount () == 50);
}
int main ( )
{ II test basic workings of strstream definitions
istrstream isl("s1 11 ) , is2("s2x", 2);
istrstream is3«(char *)lIs3"), is4«char *)lIs4 11 , 2);
assert(strcmp(isl.rdbuf()->str(), IIsl") == 0);
assert(strcmp(is3.str(), IIs3") == 0);
char buf[] = "d\Ofgh";
ostrstream osl, os2(1112345", 4);
ostrstream os3(buf, sizeof (buf), ios::app);
assert(strcmp(os2.rdbuf()->str(), 1112345 11 ) == 0);
os3 « 'e';
assert(strcmp(os3.str(), "defgh ll ) 0 &&: os3.pcount() 1);
os3.freeze(0), os3 « 'F':
assert(strcmp(os3.str(), IIdeFgh") 0 && os3.pcount() 2);
tl(), assert(O < allocs && 0 < frees):
t2(), t3();
cout « "SUCCESS testing <strstream>1I « endl:
return (0):
o
292 Chapter 11
Exercise 11.4 One constructor for class ostrstream is ostrstream(char * int, I
Background
The header <sstream> is a variation on the header <strstream>, de-
scribed in the previous chapter, to work more closely with class string. A
string object controls an in-memory character sequence, supporting op-
erations on the sequence such as assignment, concatenation, comparison,
and searching. (See Chapter 15: <string>.)
Like <strstream>, <sstream> defines three classes that cooperate to
help you read and write character sequences stored in memory:
stringbuf - stringbuf, derived from streambuf to mediate access to an in-memory
character sequence and grow it on demand (yes, the name should be
stringstreambuf for greater uniformity)
istringstream - istringstream, derived from istream to construct a stringbuf object
with input and/or output streams and to assist in extracting from the
stream
ostringstream - ostringstream, derived from ostream to construct a stringbuf object
with input and/or output streams and to assist in inserting into the
stream
If these classes sound suspiciously like the classes described in the
previous chapter, that is hardly an accident. A stringbuf object supports
much the same control over input and output streams as does a str-
streambuf object. There are just a few added capabilities:
- You can initialize the character sequence controlled by a stringbuf from
the character sequence controlled by a string, when the stringbuf is
constructed or repeatedly thereafter.
- You can initialize the character sequence controlled by a newly con-
structed string object from the character sequence controlled by a
stringbuf.
- You can specify independently, when a stringbuf is constructed,
whether the input stream is readable or the output stream is writable.
Unlike a strstreambuf object, howeve~ a stringbuf object does not let
you muck with a character sequence that is also controlled by a string
object. It simply facilitates copying between two representations private to
each object.
294 Chapter 12
The classes istringtream and ostringstream behave much like
istrstream and ostrstream in the previous chapter. They construct a
stringbuf object for you. They also provide member functions that access
the special features of a stringbuf object. In this case, that mostly involves
copying to and from string objects, as described above.
Future Directions
str As I melltioned in the previous chapter, the member function name str
has already beell changed in class string since the Informal Review Draft.
It is now data. That cleanup has yet to extend to the classes defined in
<sstream>, but it might.
wide-character As I also keep mentioning where relevant, a major change is the addition
streams of wide-character streams. (See page 119.) Class stringbuf becomes a
template class parameterized by the type of the stream element. One
instantiation, for type char, has essentially the same functionality as de-
scribed in this chapter. Another, for type wchar_t, supports streams of
elements from some large character set. Classes istringstream and os-
tringstream change along similar lines.
300 Chapter 12
Using <sstream>
You include the header <sstream> to make use of any of the classes
istringstream, ostringstream, or stringbuf. Objects of these classes let
you read and write in-memory character sequences just as if they were
conventional files, and copy character sequences. You can choose among
three patterns of access:
• read only
• write only
• read/write
I deal with each of these options in tum. For a discussion of stream-posi-
tioning operations on in-memory character sequences, see the previous
chapter. The issues are essentially the same.
read only If all you want to do is read an in-memory character sequence that is
initialized from a string object, construct an object of class istring-
stream. If you know at construction time what string object s you wish
to use, you can write:
istringstream strin(s);
The resultant stream buffer (pointed at by strin.rdbuf ()) does not sup-
port insertions. You can, however, replace the character sequence com-
pletely from another string object s2 with the call:
strin.str{s2);
The stream position is reset to the beginning of the stream. (And the
resultant stream buffer still does not support insertions.)
You can also construct an is trings tream object with an empty character
sequence, using the default constructor. Presumably, you would later sup-
ply a non-empty character sequence, as in:
istringstream strin;
strin.str{s);
write only If all you want to do is create an in-memory character sequence to be
eventually copied to a string object, construct an object of class ostring-
stream to control insertions into it. You can write:
ostringstream strout;
then insert into strout just like any other output stream. The character
sequence can grow dynamically to arbitrary length. (The actual limit is
usually I NT_MAX, defined ill <limits.h>, or when a storage allocation
request fails.) The resultant stream buffer (pointed at by strout. rdbuf ( ) }
does not support extractions, by the wa)T.
str Your goal in creating a write-only character sequence is to capture the
final result in a string object, as a rule. Write s = strout. str () to
construct a string object, initialize it to control a copy of the character
sequence, and assign it to the string object s. The two character sequences
can, of course, evolve separately thereafter.
<sstream> 301
If you want a null-terminated string, there is no real need to insert a null
character last. It will not be supplied for you when you call s = strout.
str ( ) , as above. On the other hand, you must then call s • c_str () to get
a pointer to the beginning of the character sequence. That call will supply
a terminating null character. (See Chapter 15: <string>.)
read/write If you want to create an in-memory character sequence that you can read
as well as write, you need two objects to control the input and output
streams. The classes istringstream and ostringstream are highly sym-
metric. Thus, you have three equally valid ways to do the job. If you don't
want to supply an initial character sequence, you can write:
istringstream istr(ios::in I ios::out);
ostream ostr(istr.rdbuf(»;
or:
ostringstream ostr(ios::in I ios::out);
istream istr(ostr.rdbuf(»;
or:
stringbuf sb(ios::in I ios::out);
istream istr(&sb);
ostream ostr(&sb);
All approaches cause istr to control the input stream and ostr to control
the output stream. For a discussion of end-of-file reporting in a read/write
stream of this nature, see page 277.
You can also supply an initial character sequence from a string object
s in each of these three cases:
istringstream istr(s, ios::in I ios::out);
ostream ostr(istr.rdbuf(»;
or:
ostringstream ostr(s, ios::in I ios::out);
istream istr(ostr.rdbuf(»;
or:
stringbuf sb(s, ios::in I ios::out);
istream istr(&sb);
ostream ostr(&sb);
Note that both the input and output stream positions are initially at the
beginning of the character sequence. That may not be what you intend.
Always consider whether you want to alter the output stream position
before you do anything else with such a read/ write stream.
Implementing <sstream>
sstream Figure 12.1 shows the file sstream, which implements the standard
header <sstream>. It defines the classes stringbuf, istringstream, and
ostringstream. Note that class stringbuf is based on class strstream-
bUf, described in the previous chapter. The draft C++ Standard says
(17.4.7.1) that stringbuf is derived directly from streambuf. Such indirect
derivation is permitted by the library "front matter" (17.1.5.10.3).
302 Chapter 12
Figure 12. 1: II sstream standard header
#ifndef _SSTREAM_
sstream
#define _SSTREAM_
#include <string>
#include <strstream>
II class stringbuf
class stringbuf : public strstreambuf
public:
stringbuf(ios::openmode _W = ios::in I ios::out)
: strstreambuf(O, 0, 0, _Mode(_W» {}
stringbuf(const string& _S,
ios::openmode _W= ios::in I ios::out)
: strstreambuf«char *)_S.c_str(), _S.length(), 0,
_Mode(_W» {}
virtual -stringbuf();
string str() const;
void str(const string& _S);
protected:
_Strstate _Mode(ios::openmode);
};
II class istrstream
class istringstream : public istream
public:
istringstream(openmode _W = in)
: ios(&_Sb), istream(&_Sb), _Sb(_W) {}
istringstream(const string& _S, openmode _W = in)
: ios(&_Sb), istream(&_Sb), _Sb(_S, _w) {}
virtual -istringstream();
stringbuf *rdbuf() const
{return «stringbuf *)&_Sb);
string str() const
{return (_Sb.str(»; }
void str(const string& _S)
{_Sb.str(_S); }
private:
stringbuf _Sb;
};
II class ostrstream
class ostringstream : public ostream
public:
ostringstream(openmode _W = out)
: ios(&_Sb), ostream(&_Sb), _Sb(_W) {}
ostringstream(const string& _S, openmode _W = out)
: ios(&_Sb), ostream(&_Sb), _Sb(_S, _W) {}
virtual -ostringstream();
stringbuf *rdbuf() const
{return «stringbuf *)&_Sb);
string str() const
{return (_Sb.str(»; }
void str(const string& _S)
(_Sb.str(_S); }
private:
stringbuf _Sb;
};
#endif o
<sstream> 303
Figure 12.2: II stringbuf -- stringbuf basic members
stringbu.c #include <sstream>
stringbuf::-stringbuf()
( II destruct a stringbuf
)
void tl()
{ II test stringbuf
string sO("sO"), sl("sl"), s2("s2"), s3("s3");
stringbuf sbO, sbl(ios::in), sb2(ios::out),
sb3(ios::in I ios::out);
stringbuf sbl0(sO), sbll(sl, ios::in), sb12(s2, ios::out),
sb13(s3, ios::in I ios::out);
ostream outs(&sbO);
outs « "dynamic stringbuf 0";
s3 = sbO.str();
assert(s3 == "dynamic stringbuf 0");
sbO.str(sO);
assert(sbO.str() == "sO");
outs.rdbuf(&sb2);
outs « "dynamic stringbuf 2";
assert(sb2.str() == "dynamic stringbuf 2");
outs.rdbuf(&sblO);
outs « "x";
assert(sbl0.str() == "xO");
outs.rdbuf(&sbll);
outs « "x";
assert(louts.good() && sbll.str()
outs.rdbuf(&sb12);
outs « IIX";
assert(sb12.str() == IIX");
assert (sb12.pubseekoff (2, ios::beg).offset() 2
&& sb12.str() == "x2");
void t2()
{ I I test istringstream
string SO(IISO"), sl("s1"), s2("s2"), s3("s3");
istringstream isO, isl(ios::in), is2(ios::out),
is3(ios::in I ios::out);
istringstream isl0(sO), isll(sl, ios::in),
is12(s2, ios::out), is13(s3, ios::in ios::out);
assert(isl0.rdbuf()->str() == "S0");
assert(isll.str() == "sl");
isO.str("abc");
assert(isO.str() == "abc");
isO » sO;
assert(sO == "abc ll ) ;
}
void t3()
{ I I test ostringstream
string sO("sO"), sl("sl"), S2(IS2"), s3{"s3");
ostringstream osO, osl(ios::in), os2(ios::out),
os3(ios::in I ios::out);
ostringstream os10(sO), osll(sl, ios::in),
306 Chapter 12
Continuing 0812(s2, ios::out), os13(83, ios::in ios::out);
assert(os10.rdbuf()->str() == "11);
tsstream.c
assert(os13.str() == "s3");
Part 2 of 2 osO .str( "abc");
assert(osO.str() == .... );
assert (osO.rdbuf()->pubseekoff(2, ios::beg).offset{) == 2
&:&: osO.str() == lIab");
osO « "Cde";
assert(osO.str{) == lIabCde");
}
int maine)
( II test basic workings of stringstream definitions
t1 ();
t2 ();
t3();
cout « "SUCCESS testing <sstream>1I « endl;
return (0);
o
Testing <sstream>
tsstream. c Figure 12.7 shows the file tsstream. c. It tests the basic properties of the
classes defined in <sstream>. It does so in three groups:
• s tringbuf objects (t 1 ( ) )
• istringstream objects (t2 ( »
B ostringstream objects (t3 ( »
The function main () simply performs tllese groups of tests in the order
shown. If all goes well, the program prints:
SUCCESS testing <sstre~
Exercises
Exercise 12. 1 Can you construct a stringbuf object that permits neither insertions or
deletions? If so, does it ever make sense to do so?
Exercise 12.2 Alter the classes defined in <strstream> to subsume the functionality of
the classes defined in <sstream>. Would you change anything else about
the classes you are redefining?
Exercise 12.3 Alter the classes defined in <sstream> to subsume the functionality of the
classes defined ill <strstream>. Would you omit any features?
Exercise 12.4 [Harder] Implement stringbuf without using the code for strstreambuf.
Exercise 12.5 [Very hard] Design alld implement a version of stringbuf that allows
simultaneous control of a character sequence with a string object. Why
would you want such a capability?
Chapter 13: <fstream>
Background
The header <fstream> defines half a dozen classes. Three of these
cooperate to help you read and write files that you open by name:
filebuf - filebuf, derived from streambuf to mediate access to an external
stream of characters read from or written to a file
ifstream - ifstream, derived from istream to construct a filebuf object and to
assist in extracting from the stream
ofstream - ofstream, derived from ostream to construct a filebuf object and to
assist in inserting into the stream
The other three classes defined in <fstream> cooperate to help you read
and write files controlled by an object of type FiLE, declared in <stdio. h>:
stdiobuf - stdiobuf, derived from streambuf to mediate access to a stream of
characters read from or written to an external stream under control of a
FILE object
istdiostream - istdiostream, derived from istream. to construct a stdiobuf object
and to assist in extracting from the stream
ostdiostream - ostdiostream, derived from ostream to construct a stdiobuf object
and to assist in inserting into the stream
cin Reading and writing files is an important part of iostreams. For many
cout C++ programs, it is the sole means of communication between a program
and the outside world. The standard stream objects, such as cin and cout,
are conventionally associated with external streams. (See the next chapter.)
Even in this era of window-oriented interfaces, reading and writing files
remains important. Communication between two programs, between a
program and a window, or between a program and a special device are all
often made to look like conventional file reads and writes. The classes
defined in <fstream> are the preferred agents for controlling such file
operations.
You do not, in principle, need two sets of classes for accessing files with
iostreams. The Standard C library function fopen, declared in <stdio. h>,
associates a named file with a FILE object. The same header declares
fclose, for removing the association. You can associate a FiLE object with
a stdiostream object, as described above, and you're in business. Why the
extra machinery?
308 Chapter 13
history The answer is largely historical. The iostreams package evolved alongside
the Standard C library more than atop it. Both spent their earliest days
hosted on the UNIX operating system, a particularly friendly environment
for reading and writing files. As they have spread to other systems, both
have also profited from the influence of UNIX on more modern operating
systems. Thus, iostreams and the Standard C library have a common
heritage, and many common architectural features, but they nevertheless
grew up separatel)'.
As a consequence, mixing iostrearns and C stream operations has often
led to an uneasy alliance. A filebuf object often performs reads and writes
directl)', using low-level operating system calls and managing an in-mem-
ory buffer directl)'. A stdiobuf object, by contrast, typically calls on the
higher-level functions declared in <stdio.h>. The overhead per character
can be much higher and buffering can occur in two different places within
the program.
c++ programmers habitually favor the former over the latter, if only for
better performance. They introduce stdiobuf objects only when obliged
to mix iostreams and C stream reads or writes to the same file. And then
they fret over the uncertainties that inevitably arise with double buffering.
Input can be consumed in greater chunks than you expect, by the two
agents. Or output from the two agents can get pasted together in bigger
chunks than you intend. Often, the only safe fix is to make stdiobuf
operations completely unbuffered. And that often penalizes performance
even more.
responses The draft c++ Standard addresses these historical problems:
• It defines the semantics of class filebuf as if it performs reads and
writes through a FILE object. The draft C++ Standard thus rides atop the
detailed descriptions of file operations from the C Standard. And it gives
quick answers to many questions about the effect of mixing iostreams
and C stream operations.
• It defines class filebuf in such a way that no FILE object is directly
visible. The draft c++ Standard thus permits an implementation of
filebuf atop the Standard C libraf)T, without mandating it.
• It retains class stdiobuf, which is defined explicitly in terms of a visible
FILE object. Operations are unbuffered by default, so programs have no
synchronization surprises.
• It nevertheless provides member functions to control whether stdiobuf
operations can be buffered, if performance is more important to you than
tight synchronization between iostreams and C stream operations.
• It specifies that the standard objects cin, cout, cerr, and clog behave
as if they designate unbuffered stdiostream objects, without requiring
exactly that implementation. (See next chapter.)
The basic idea is to better define the relationship between iostreams and C
stream operations, and to provide as a default less surprising semantics for
<fstream> 309
mixed operations. Nevertheless, the draft c++ Standard does not require
that existing implementations of iostreams be rewritten purely in terms of
calls to Standard C library functions.
open One example of shared semantics is the way you open a file by name.
Thefilebufmemberfunctionopen(const char *, ios::openmode) is
the agent that does the job. It effectively calls fopen, declared in <stdio. h>.
To do so, it must map its second argument to the kind of mode string
acceptable to fopen. Thus, for example, ios: : in becomes r". The mem-
II
If the resulting file is not a null pointer and mode « ios: :ate is nonzero,
the function calls fseek (file, 0, SEEK_END). If that function returns a null
pointer, the function calls close () and returns a null pointer. Otherwise, the
function returns this.
The macro SEEK_END is defined, and the function signatures fopen(const
char*, const char*) andfseek(FlLE*, long, int> are declared, in
<stdio.h>.
17.4.8.1.5 filebuf: :open(const char*, ios: : open_mode)
open 1/ filebuf* open(const char* s, ios::open_mode mode); optional
Returns open ( s, ( ios: : openmode) mode) .
17.4.8.1.6 filebuf: :close()
close filebuf* close();
If file is a null pointer, returns a null pointer. Otherwise, if the call
fclose (file) returns zero, the function stores a null pointer in file and
returns this. Otherwise, it returns a null pointer.
The function signature fclose (FILE*) is declared, in <stdio.h>.
17.4.8.1.7 filebuf: :overflow(int)
overflow 1/ virtual int overflow(int c = EOF); inberited
Appends the character designated by c to the output sequence, if possible, in
one of three ways:
• If c ! = EOF and if either the output sequence has a write position available
or the function makes a write position available (in an unspecified manner),
the function assigns cto *pnext++. The function signals success by returning
(unsigned char) c.
312 Chapter 13
• If c ! = EOF, the function appends c directly to the associated output
sequence (as described below). If pbeg < pnext, the pnext - pbeg
characters beginning at pbeg are first appended directly to the associated
output sequence, beginning with the character at pbeg. The function signals
success by returning (unsigned char) c.
• If c == EOF, there is no character to append. The function signals success
by returning a value other than EOF.
If the function can succeed in more than one of these ways, it is unspecified
which way is chosen. The function can alter the number of write positions
available as a result of any call.
The functionretums EOF to indicate failure. If fileis anuU pointer, the function
always fails.
To append a character x directly to the associated output sequence, the function
evaluates the expression:
fputc(x, file) == x
which must be nonzero. The function signature fputc (int I FILE*) is
declared in <stdio.h>.
17.4.8.1.8 filebuf: :pbackfail (int)
pbackfail II virtual int pbackfail(int c = EOF); inherited
Puts back the character designated by c to the input sequence, if possible, in one
of four ways:
• If c ! = EOF and if either the input sequence has a putback position available
or the function makes a putback position available (in an unspecified manner),
the function assigns c to * - - gnext. The function signals success by returning
(unsigned char) c.
• If c J= EOF and if no putback position is available, the function puts back c
directly to the associate input sequence (as described below). The function
signals success by returning (unsigned cha» c.
• If c == EOF and if either the input sequence has a putback position available
or the function makes a putback position available, the function assigns gnext
- 1 to gnext. The function signals success by returning (unsigned
char) c.
• If c == EOF, if no putback position is available, and if the function can
determine the character x immediately before the current position in the
associated input sequence (in an unspecified manner), the function puts back
x directly to the associated input sequence. The function signals success by
returning a value other than EOF.
If the function can succeed in more than one of these ways, it is unspecified
which way is chosen. The function can alter the number of putback positions
available as a result of any call.
The function returns EOF to indicate failure. If file is anuU pointer, the function
always fails.
To put back a character x directly to the associated input sequence, the function
evaluates the expression:
ungetc(x, file) == x
which must be nonzero. The function signature ungetc (int, FILE*) is
declared in <stdio.h>.
17.4.8.1.9 f i lebuf: : underflow ( )
underflow II virtual int underflow(); inherited
Reads a character from the input sequence, if possible, without moving the
stream position past it, as follows:
<fstream> 313
• H the input sequence has a read position available the function signals success
by returning (unsigned char) *gnext.
• Otherwise, if the function can determine the character x at the current position
in the associated input sequence (as described below), it signals success by
returning (unsigned char) x. If the function makes a read position avail-
able, it also assigns x to * gnext.
The function can alter the number of read positions available as a result of any
call.
The function returns EOF to indicate failure. If file is a null pointer, the function
always fails.
To determine the character x (of type int) at the current position in the
associated input sequence, the function evaluates the expression:
(x = ungetc(fgetc(file), file» != EOF
which must be nonzero. The function signatures fgetc (FILE*) and
ungetc(int, FILE*) are declared in <stdio.h>.
17.4.8.1.10 filebuf: :uflow()
uflow II virtual int uflow(); inherited
Reads a character from the input sequence, if possible, and moves the stream
position past it, as follows:
• H the input sequence has a read position available the function signals success
by returning (unsigned char) *gnext++.
• Otherwise, if the function can read the character x directly from the associated
input sequence (as described below), it signals success by returning (un-
signed char) x. If the function makes a read position available (in an
unspecified manner), it also assigns xto *gnext.
The function can alter the number of read positions available as a result of any
call.
The function returns EOF to indicate failure. If file is a null pointer, the function
always fails.
To read a character into an object x (of type int) directly from the associated
input sequence, the function evaluates the expression:
(x = fgetc(file» != EOF
which must be nonzero. The function signature fgetc (FILE*) is declared in
<stdio.h>.
17.4.8.1.11 filebuf: :xsgetn(char*, int)
xsgetn II virtual int xsgetn(char* s, int n); inherited
Behaves the same as streambuf: :xsgetn(char*, int).
17.4.8.1.12 filebuf: :xsputn(const char*, int)
xsputn 1/ virtual int xsputn(const char* s, int n); inherited
Behaves the same as streambuf : : xsputn (char*, int).
17.4.8.1.13 filebuf: :seekoff(streamoff, ios: :seekdir,
ios : : openmode )
seekoff 1/ virtual streampos seekoff(streamoff off, ios::seekdir way,
1/ ios::openmode which = ios::in I ios::out); inherited
Alters the stream position within the controlled sequences, if possible, as
described below. The function returns a newly constructed streampos object
that stores the resultant stream position, if possible. If the positioning operation
fails, or if the object cannot represent the resultant stream position, the object
stores an invalid stream position.
314 Chapter 13
If file is a null pointer, the positioning operation fails. Otherwise, the function
determines one of three values for the argument whence, of type int:
• If way == ios: :beg, the argument is SEEK_SET;
• If way == ios:: cur, the argument is SEEK_CUR;
• If way == ios:: end, the argument is SEEK_END.
The function then calls fseek(file, off, whence) and, if that function
returns nonzero, the positioning operation fails.
The macros SEEK_SET, SEEK_CUR, and SEEK_END are defined, and the func-
tion signature fseek(FILE*, long, int) is declared, in <stdio.h>.
17.4.8.1.14 filebuf: :seekpos (streampos, ios: :openmode)
seekpos II virtual streampos seekpos(streampos sp,
II ios::openmode which = ios::in I ios::out); inherited
Alters the stream position within the controlled sequences, if possible, to
correspond to the stream position stored in S1' .pos and sp. fp.108) The function
returns a newly constructed streampos object that stores the resultant stream
position, if possible. If the positioning operation fails, or if the object cannot
represent the resultant stream position, the object stores an invalid stream
position.
If file is a null pointer, the positioning operation fails.
17.4.8.1.15 filebuf: :setbuf(char*, int)
setbuf II virtual streambuf* setbuf(char* s, int n); inherited
Makes the array of n (single-byte) characters, whose first element is designated
by s, available for use as a buffer area for the controlled sequences, if possible.
If f i l e is a null pointer, the function returns a null pointer. Otherwise, if the
call setvbuf (file, s, _IOFBF, n) is nonzero, the function returns a null
pointer. Otherwise, the function returns *this.
The macro _IOFBF is defined, and the function signature setvbuf (FILE*,
char*, int, size_t) is declared, in <stdio.h>.
17.4.8.1.16 f i lebuf: : sync ( )
sync II virtual int sync{); inherited
Returns zero if file is a null pointer. Otherwise, the function returns
fflush( file).
The function signature fflush (FILE*) is declared in <stdio. h>.
17.4.8.2 Class ifstream
Class class ifstream : public istream {
ifstream public:
ifstream{);
ifstream{const char* s, openmode mode in);
virtual -ifstream{);
filebuf* rdbuf() const;
int is_open{);
void open(const char* s, openmode mode = in);
II void open(const char* s, open_mode mode); optional
void close{);
private:
1/ filebuf fbi exposition only
};
The class if stream is a derivative of i stream that assists in the reading of
named files. It supplies a filebuf object to control the associated sequence.
For the sake of exposition, the maintained data is presented here as:
<fstream> 315
• filebuf fb, the filebuf object.
17.4.8.2.1 ifstream: :ifstream()
constructor ifstream() ;
Constructs an object of class ifstream, initializing the base class with is-
tream(&:fb) .
17.4.8.2.2 ifstream:: ifstream(const char*, openmode)
constructor ifstream(const char* s, openmode mode = in);
Constructs an object of class ifstream, initializing the base class with is-
tream(&fb), then calls open(s, mode).
17.4.8.2.3 ifstream: : ~ifstream()
destructor virtual -ifstream();
Destroys an object of class ifstream.
17.4.8.2.4 ifstream: :rdbuf()
rdbuf filebuf* rdbuf() const;
Returns (filebuf*) &fb.
17.4.8.2.5 ifstream:: is_open ( )
is_open int is_open();
Returns fb. is_open ( ).
17.4.8.2.6 ifstream: : open (const char*, opemnode)
open void open(const char* s, openmode mode = in);
Calls fb.open(s, mode). If the call is_open() returns zero, calls set-
state (failbit).
17.4.8.2.7 ifstream: : open (const char*, open_mode)
open II void open(const char* s, open_mode mode); optional
Calls open ( s, (opemnode) mode) .
17.4.8.2.8 ifstream: :close()
close void close();
Calls fb. close () and, if that function returns zero, calls setstate (fail-
bit).
17.4.8.3 Class ofstream
Class class of stream : public ostream {
ofstream public:
ofstream();
ofstream(const char* s, openmode mode out);
virtual -ofstream();
filebuf* rdbuf() const;
int is_open();
void open(const char* s, openmode mode out I trunc);
II void open(const char* s,
open_mode mode); optional
void close();
private:
II filebuf fbI exposition only
);
The class ofstream is a derivative of ostream that assists in the writing of
named files. It supplies a filebuf object to control the associated sequence.
For the sake of exposition, the maintained data is presented here as:
316 Chapter 13
• filebuf fb, the filebuf object.
17.4.8.3.1 of stream: :ofstream{)
constructor ofstream() ;
Constructs an object of class ofstream, initializing the base class with os-
tream{&:fb) .
17.4.8.3.2 of stream: : of stream {const char*, openmode)
constructor ofstream(const char* B, openmode mode = out);
Constructs an object of class ofstream, initializing the base class with os-
tream{&:fb), then calls open{s, mode).
17.4.8.3.3 ofstream: : -ofstream{ )
destructor virtual -ofstream();
Destroys an object of class of stream.
17.4.8.3.4 of stream: : rdbuf ()
rdbuf filebuf* rdbuf() const;
Returns (filebuf*) &:fb.
17.4.8.3.5 of stream: : is_open { )
is_open int is_open();
Returns fb. is_open { ).
17.4.8.3.6 of stream : : open {const char*, openmode)
open void open(const char* B, openmode mode = out);
Calls fb. open ( s, mode). If is_open () is then false, calls set-
state (failbit).
17.4.8.3.7 ofstream: : open {const char*, open_mode)
open II void open(const char* B, open_mode mode); optional
Calls open (s, (opemnode) mode) .
17.4.8.3.8 ofstream: : close ()
close void close();
Calls fb. close () and, if that function returns zero, calls setstate ( fail-
bit).
17.4.8.4 Class stdiobuf
Class class stdiobuf : public streambuf
stdiobuf public:
stdiobuf(FXLE* file_srg);
virtual -stdiobuf();
int buffered() const;
void buffered(int buf_fl);
protected:
II virtual int overflow(int c = EOF); inherited
II virtual int pbackfail(int c = EOF); inherited
II virtual int underflow(); inherited
II virtual int uflow(); inherited
II virtual int xsgetn(char* B, int n); inherited
II virtual int xsputn(const char* B, int n); inherited
II virtual streampos seekoff(streamoff off, ios::seekdir way,
II ios::openmode wbicb =
ios::in I ios::out); inherited
II virtual streampos seekpos(streampos BP,
II ios::openmode wbicb =
ios::in I ios::out); inherited
<fstream> 317
II virtual streambuf* setbuf(char* s, int D); inherited
II virtual int sync(); inherited
private:
II FILE* file; exposition only
II int is_buffered; exposition only
};
The class stdiobuf is derived from streambuf to associate both the input
sequence and the output sequence with an externally supplied object of type
FiLE. Type FiLE is defined in <stdio.h>. For the sake of exposition, the
maintained data is presented here as:
• FILE * file, points to the FiLE associated with the stream buffer;
• int is_buffered, nonzero if the stdiobuf object is buffered, and hence
need not be kept synchronized with the associated file (as described below).
The restrictions on reading and writing a sequence controlled by an object of
class stdiobuf are the same as for an object of class filebuf.
If an stdiobuf object is not buffered and file is not a null pointer, it is kept
synchronized with the associated file, as follows:
• the call sputc (e) is equivalent to the call fputc (e, file);
• the call sputbackc (e) is equivalent to the call ungetc (e, file);
• the call sbumpc () is equivalent to the call fgetc (file) •
The functions fgetc(FiLE*), fputc(int, FILE*), and ungetc(int,
FiLE*) are declared in <stdio. h>.
17.4.8.4.1 stdiobuf:: stdiobuf (FILE*)
constructor stdiobuf (FILE* file_arg);
Constructs an object of class stdiobuf, initializing the base class with
streambuf () , and initializing file to file_srg and is_buffered to zero.
17.4.8.4.2 stdiobuf: : -stdiobuf ()
destructor virtual -stdiobuf();
Destroys an object of class stdiobuf.
17.4.8.4.3 stdiobuf: :buffered()
buffered int buffered() const;
Returns a nonzero value if is_buffered is nonzero.
17.4.8.4.4 stdiobuf: : buffered (int)
buffered void buffered(int bUf_fl);
Assigns bUf_fl to is_buffered.
17.4.8.4.5 stdiobuf : : overflow (int)
overflow II virtual int overflow(int c = EOF); inherited
Behaves the same as filebuf: :overflow(int), subject to the buffering
requirements specified by is_buffered.
17.4.8.4.6 stdiobuf: :pbackfail(int)
pbackfail II virtual int pbackfail{int c = EOF); inherited
Behaves the same as filebuf: :pbackfail (int), subject to the buffering
requirements specified by is_buffered.
17.4.8.4.7 stdiobuf: : underflow ( )
underflow II virtual int underflow{); inherited
Behaves the same as filebuf: : underflow ( ), subject to the buffering re-
quirements specified by is_buffered.
318 Chapter 13
17.4.8.4.8 stdiobuf: :uflow()
uflow II virtual int uflow(); inherited
Behaves the same as f ilebuf : : uflow ( ), subject to the buffering require-
ments specified by is_buffered.
17.4.8.4.9 stdiobuf: : xsgetn(char* , int)
xsgetn II virtual int xsgetn(char* s, int n); inherited
Behaves the same as streambuf: : xsgetn (char*, int).
17.4.8.4.10stdiobuf::xsputn(const char*, int)
xsputn II virtual int xsputn(const char* B, int n); inherited
Behaves the same as streambuf: : xsputn(char* , int).
17.4.8.4.11 stdiobuf: : seekoff (streamoff, ios:: seekdir,
ios::openmode)
seekoff II virtual streampos seekoff(streamoff off, ios::seekdir way,
II ios::openmode which = ios::in I ios::out); inherited
Behaves the same as filebuf: : seekoff (streamoff, ios:: seekdir,
ios : : openmode)
17.4.8.4.12 stdiobuf: : seekpos (streampos, ios:: openmode)
seekpos II virtual streampos seekpos(streampos BP,
II ios::openmode which = ios::in I ios::out); inherited
Behaves the same as filebuf: : seekpos (streampos, ios: :openmode)
17.4.8.4.13 stdiobuf:: setbuf (char*, int)
setbuf II virtual streambuf* setbuf(char* s, int n); inherited
Behaves the same as filebuf: : setbuf (char*, int)
17.4.8.4.14stdiobuf: : sync ( )
sync II virtual int sync(); inherited
Behaves the same as filebuf: : sync ()
17.4.8.5 Class istdiostream
Class class istdiostream : public istream
istdiostream public:
istdiostream(FILE* file_srg);
virtual -istdiostream();
stdiobuf* rdbuf() const;
int buffered() const;
void buffered(int bUf_fl);
private:
/ / stdiobuf fbI expos1 tion on~y
);
The class istdiostreamis a derivative of istream that assists in the reading
of files controlled by objects of type FILE. It supplies a stdiobuf object to
control the associated sequence. For the sake of exposition, the maintained data
is presented here as:
• stdiobuf fb, the stdiobuf object.
17.4.8.5.1 istdiostream:: istdiostream(FILE*)
constructor istdiostream(FILE* file_srg);
Constructs an object of class istdiostream, initializing the base class with
istream(&fb) and initializing fb with stdiobuf (file_srg).
<fstream> 319
17.4.8.5.2 istdiostream:: -istdiostream()
destructor virtual -istdiostream();
Destroys an object of class istdiostream.
17.4.8.5.3 istdiostream: : rdbuf ()
rdbuf stdiobuf* rdbuf() const;
Returns (stdiobuf*) &.fb.
17.4.8.5.4 istdiostream: :buffered()
buffered int buffered() const;
Returns a nonzero value if is_buffered is nonzero.
17.4.8.5.5 istdiostream: :buffered(int)
buffered void buffered(int bUf_fl);
Assigns bUf_fl to is_buffered.
17.4.8.6 Class ostdiostream
Class class ostdiostream : public ostream
ostdiostream public:
ostdiostream(FILE* file_srg);
virtual -ostdiostream();
stdiobuf* rdbuf() const;
int buffered() const;
void buffered(int bUf_fl);
private:
/ / stdiobuf fbi exposition only
};
The class ostdiostream is a derivative of ostream that assists in the writing
of files controlled by objects of type FILE. It supplies a stdiobuf object to
control the associated sequence. For the sake of exposition, the maintained data
is presented here as:
• stdiobuf fb, the stdiobuf object.
17.4.8.6.1 ostdiostream: : ostdiostream (FILE* )
constructor ostdiostream(FILE* file_srg);
Constructs an object of class ostdiostream, initializing the base class with
ostream(&.fb) and initializing fb with stdiobuf (file_arg).
17.4.8.6.2 ostdiostream:: -ostdiostream()
destructor virtual -ostdiostream();
Destroys an object of class ostdiostream.
17.4.8.6.3 ostdiostream: : rdbuf ( )
rdbuf stdiobuf* rdbuf() const;
Returns (stdiobuf*) &.fb.
17.4.8.6.4 ostdiostream: : buffered ( )
buffered int buffered() const;
Returns a nonzero value if is_buffered is nonzero.
17.4.8.6.5 ostdiostream: :buffered(int)
buffered void buffered(int bUf_fl);
Assigns bUf_fl to is_buffered.
320 Chapter 13
Footnotes:
Footnotes 108) The function may, for example, call fsetpos(file, &sp.fp) and/or
f seek (f il e, sp. pos, SEEK_SET), declared in <stdio . h>.
Future Directions
showmany As I mentioned in conjunction with the header <streambuf> class
streambuf has an added virtual member function. (See page 159.) The
public access function for it is called showmany. It endeavors to tell you how
many characters you can safely extract with no fear of blocking while
waiting for additional input. The derived classes filebuf and stdiobuf
should have nontrivial overrides for this virtual member function.
wide-character Once again, I note that a major change is the addition of wide-character
streams streams. (See page 119.) The classes filebuf and stdiobuf become tem-
plate classes parameterized by the type of the stream element. One instan-
tiation, for type chart has essentially the same functionality as described in
this chapter. Another, for typewchar_t, supports streams of elements from
some large character set. Classes ifstream, ofstream, istdiostream, and
ostdiostream change along similar lines.
Using <fstream>
You include the header <fstream> to make use of any of the classes
ifstream, ofstream, filebuf, istdiostream, ostdiostream, or
stdiobuf. Objects of these classes let you read and write conventional files.
You can open files by name and control them, or control files already
opened under control of objects of type FiLE. For each approach, you can
choose among three patterns of access:
• read only
• write only
• read/write
I deal with each of these options in tum, first for files you open by name.
read only If all you want to do is open and read an existing text file whose name
you know, construct an object of class ifstream. If you know at construc-
tion time what null-terminated file name s you wish to use, you can write:
ifstream fines);
if (fin.is_open(»
<file opened successfully>
is_open If the file is not opened successfully, subsequent extractions will fail. Note,
however, that the conventional tests for failure, I fin or fin 1= 0, will not
be false until after you essay such an extraction. That's why I encourage
you to make the explicit test fin. is_open ( ) immediately afterthe object is
constructed.
<fstream> 321
close The resultant stream buffer (pointed at by fin. rdbuf ( » does not sup-
open port insertions. You can, however, close any currently open file, then open
the file s2 for reading with the two calls:
fin.close(), fin.open(s2);
Naturall)T, you should once again test whether the open succeeded, as
above. The stream position is reset to the beginning of the newly opened
stream. (And the resultant stream buffer still does not support insertions.)
You can also construct an ifstream object with no open file, using the
default constructor. Presumabl)T, you would later open an existing text file
for reading, as in:
ifstream fin;
fin.open(s);
if (fin.is_open(»
<file opened successfully>
Destroying an ifstream objectcloses any open file associated with it.
open The code I have shown so far always opens a text file, for reading only.
modes A text file can be subject to a certain amount of interpretation, such as
mapping the sequence carriage return/line feed to just line feed (newline).
A binary file, on the other hand, delivers each byte from the file unchanged
as a char value. To read a binary file, to make a file writable as well, or to
invoke various other options when you open a file, you have to specify an
explicit open-mode argument. (Naturally enough, it has type ios: : open-
mode.) For all member functions that take a file-name argument s, the
open-mode mode immediately follows. The first example, above, is actually
equivalent to:
ifstream fines, ios::in);
if (fin.is_open(»
<file opened successfully>
You have a number of options for the value of mode:
• ios : : in, to open an existing text file for reading
• ios:: out I ioe:: trunc, to create a text file or to open and truncate an
existing text file for writing
• ios:: out I ios:: app, to create a text file or to open an existing text
file for writing, where each write occurs at the end of the file
• ios : : in I ios : : binary, to open an existing binary file for reading
• ios:: out I ios:: trunc I ios:: binary, to create a binary file or to
open and truncate an existing binary file for writing
• ios:: out I ios:: app I ios: : binary, to create a binary file or to open
an existing binary file for writing, where each write occurs at the end of
the file
• ioe:: in I ios:: out, to open an existing text file for reading and
writing
• ios:: in I ios:: out I ios:: trunc, to create a text file or to open and
truncate an existing text file for reading and writing
322 Chapter 13
• ios:: in I ios:: out I ios:: app, to create a text file or to open an
existing text file for reading and writing, where each write occurs at the
end of the file
• ios : : in I ios:: out I ios:: binary, to open an existing binary file
for reading and writing
• ios:: in I ios:: out I ios:: trunc I ios:: binary, to create a
binary file or to open and truncate an existing binary file for reading and
writing
• ios::in I ios::out I ios::app I ios::binary,tocreateabinary
file or to open an existing binary file for reading and writing, where each
write occurs at the end of the file
If you also set ios: :ate in mode, the file is positioned at end-of-file
immediately after it is opened.
write only If all you want to do is create a new text file - or truncate an existing
text file - then open it for writing, construct an object of class ofstream to
control insertions into it. You can write:
ofstream fout(s);
if (fout.is_open(»
<file opened successfully>
then insert into fout just like any other output stream. As with class
ifstream, you can follow the file name s with a mode argument. If you omit
the mode argument, as above, it defaults to ios: : out.
You can also construct an ofstream object with the default constructor
and later create it for writing, as in:
ofstream fout;
fout.open(s);
if (fout.is_open(»
<file opened successfully>
In either case, the resultant stream buffer (pointed at by fout. rdbuf ( »
does not support extractions. And, of course, destroying an ofstream
object closes any open file associated with it.
read/write If you want to open a file that you can read as well as write, you need
two objects to control the input and output streams. The classes ifstream
and ofstream are highly symmetric, at least in this regard. Thus, you have
three equally valid ways to do the job. If you don't want to open a file
initiall)', you can write:
ifstream ifile;
ostream ofile(ifile.rdbuf(»;
or:
ofstream ofile;
istream ifile(ofile.rdbuf(»;
or:
filebuf fb;
istream ifile(&fb);
ostream ofile(&fb);
<fstream> 323
All approaches cause ifile to control the input stream and ofile to
control the output stream.
You can also open a file s in each of these three cases. Since the default
values for the mode argument rarely make sense here, I show the argument
explicitly in each case:
if,tream ifile(s, mode);
ostream ofile(ifile.rdbuf(»;
if (ifile.is_open(»
<file opened successfully>
or:
ofstream ofile(s, mode);
istream ifile(ofile.rdbuf(»;
if (ofile.is_open(»
<file opened successfully>
or:
filebuf fbI
istream ifile(&fb);
ostream ofile(&fb);
if (fb.open(s, mode»
<file opened successfully>;
Note that the last test for a successful open differs from the earlier ones. As
usual, when the filebuf object is destroyed, any open file associated with
it is closed.
FILE The classes istdiostream, ostdiostream, and stdiobuf provide addi-
tional capability within the header <fstream>. They let you control files
already opened under control of an object of type FILE. For example, the
function fopen, declared in <stdio.h>, returns a non-null pointer to FILE
when it successfully opens a file. Numerous other functions, declared in
the same heade~ support C stream reads and writes to the opened file.
The same header also declares three well known objects of type pointer
to FILE that control the three standard streams:
stdin - stdin, controlling the standard input stream
stdout - stdout, controlling the standard output stream
stderr - stderr, controlling the standard error stream
The header <iostream> declares several istreamand ostreamobjects that
work in concert with these objects to support iostreams operations on the
standard streams. (See next chapter.) You can nevertheless use the facilities
in <fstream> to control, sa)', stdout with an additional object you con-
struct.
As usual, there are three patterns of access to discuss: read only, write
only, and read/write. I cover them in order.
read only If all you want to do is read a stream controlled by a FILE object,
construct an object of class istdiostream. You must know at construction
time the argument value pf, of type pointer to FILE. You can write:
istdiostream fin(pf)i
324 Chapter 13
If pf is a null pointer, or if the stream it controls cannot be read, all
subsequent extraction operations will fail. You cannot, however, test
whether fin is associated with an open file.
When fin is destroyed, the stream *pf is not closed. Nor should you
close the stream, by calling fclose (pf), before fin is destroyed. The call
discredits pf, so even a subsequent attempt to access the pointer itself can
cause a program to terminate abnormally. Worse, subsequent attempts to
control the file may do all sorts of insane things that are not diagosed.
buffered You can control the degree of buffering within fin. Initiall)', fin.buff-
ered() returns zero, indicating that no buffering occurs. Put simpl)', you
can alternate the calls fin. get () and fgetc (pf) and read alternate char-
acters from the file.
Once you call fin.buffered(l), however, fin.buffered(l) returns a
nonzero value. Thereafter, buffering may occur. Put simpl)', the call fin.
get () may encourage the stream buffer associated with fin to gobble an
arbitrary number of characters, not just the one you requested. A sub-
sequent call to fgetc (pf) will not necessarily deliver the next character
you would expect.
If you resist the temptation to access *pf directly, buffering causes no
problems. On the contrary, it offers the controlling stream buffer the oppor-
tunity to improve performance, sometimes considerably. A wise rule of
thumb, therefore, is never to enable buffering for a file accessed both via a
stream buffer and via C stream function calls. If the stream buffer is the sole
agent accessing the file, always enable buffering.
write only If all you want to do is write a stream controlled by a FILE object,
construct an object of class ostdiostream. You must know at construction
time the argument value pf, of type pointer to FILE. You can write:
ostdiostream fout(pf);
If pf is a null pointer, or if the stream it controls cannot be written, all
subsequent insertion operations will fail. As with an istiodstream object,
you cannot test whether fout is associated with an open file. The same
remarks also apply about not closing the file until fout is destroyed.
Equally, the same considerations apply about when to buffer, or not to
buffer, a stream associated with an ostdiostream object.
read/write Finally, if you want to both read and write a stream controlled by a FILE
object, you need two objects to control the input and output streams. The
classes istdiostreamand ostdiostreamare highly symmetric. Thus, you
have three equally valid ways to do the job:
istdiostream ifile(pf);
ostream ofile(ifile.rdbuf(»;
or:
ostdiostream ofile(pf);
istream ifile(ofile.rdbuf(»;
or:
<fstream> 325
stdiobuf sb(pf);
istream ifile(&sb);
ostream ofile(&sb);
In the third case, you enable buffering by calling sb.buffered (1).
stream Earlier in this chapter, I discussed the limitations on positioning within
positioning files. (See page 309.) For a discussion of stream-positioning operations in
general, see page 160. Your safest bet, as always, is to memorize a file
position you want to return to, as an object of type streampos. Later on in
the program, while the file is still open, you can use the value stored in this
object to return to the memorized file position.
For a binary file that is not too large, you can represent a stream position
as an object of type streamoff. You can thus perform arithmetic, on byte
displacements from the beginning of a file, to determine new stream
positions. The UNIX operating system represents text files the same as
binal)T. Hence, it extends the same latitude in stream positioning to all files,
not just binary. But few other systems share this simplicity: Don't write
portable code that counts on it.
A file opened both for reading and writing requires intervening stream-
positioning requests when switching from reading to writing, or back.
Again, some systems may relax this requirement, but don't count on it in a
portable program.
setbuf Finally, the streambuf virtual member functions setbuf and sync are
sync given nontrivial semantics in the derived class filebuf. The former, how-
ever, is defined in terms of the function setvbuf, declared in <stdio.h>,
which does not itself promise much. And the latter is generally called as
often as necessary in the normal course of business. (See page 221.) I
recommend, therefore, that you not call either pubsetbuf or pubsync, the
public member functions that call these virtual member functions on your
behalf.
Implementing <fstream>
fstream Figure 13.1 shows the file fstream, which implements the standard
header <fstream>. It defines the classes filebuf, ifstream, of stream,
stdiobuf, istdiostream, and ostdiostream. I begin with several notes
on class filebuf, which is the workhorse class for this header.
_Filet The incomplete type declaration struct _Filet is an alias for the type
FiLE, declared in <stdio.h>. The header <fstream> is not permitted to
include <stdio.h>, but it needs to declare parameters and member func-
tion return values compatible with type FILE. A secret synonym solves the
problem. My implementation of the Standard C library (Pla92) introduces
_Filet for a similar reason. For another implementation, you may have to
alter the name, or introduce extra machinery in the internal header
<yxvals .h>, to achieve the same effect. (See AppendiX A: Interfaces.)
326 Chapter 13
filebuf *filebuf::close()
{ II close a file
if (_File 1= 0 && fclose(_File) 0)
( II note successful close
_:Init();
return (this);
else
return (0);
int filebuf::underflow()
( I I try to peek at input
return (gptr() 1= 0 && gptr() < egptr()
? *_Gn()
_File == 0 ? EOF : ungetc(fgetc(_File), _File»;
int filebuf::uflow()
( I I try to consume input
return (gptr() 1= 0 && gptr() < egptr(}
? *_Gn()++
_File == 0 ? EOF : fgetc(_File)};
int filebuf::sync()
{ II synchronize buffer with file
return (_File == 0 ? 0 fflush(_File»;
succeeds in opening the file, it reinitializes the f ilebuf object to control the
associated FiLE object.
ifstream.c All the remaining source files needed to implement the header
ofstream.c <fstream> are trivial. Figure 13.4 shows the file ifstream. c, which defines
stdiobuf.c the destructor for class ifstream. Figure 13.5 shows the file ofstream.c,
istdiost.c which defines the destructor for class ofstream. Figure 13.6 shows the file
ostdiost.c stdiobuf. c, which defines the destructor for class stdiobuf. Figure 13.7
shows the file istdiost .e, which defines the destructor for class istios-
tream. And Figure 13.8 shows the file ostdiost .e, which defines the
destructor for class ostdiostream.
334 Chapter 13
int maine)
{ II test basic workings of fstream definitions
ifstream ifs;
ofstream ofs;
const char *tn = tmpnam(NOLL);
assert(tn != NULL);
II test closed file closing
assert(!ifs.is_open() && !ifs.fail(»;
ifs.close();
assert(ifs.fail() && ifs.rdbuf()->close() 0);
assert(!ofs.is_open() && lofs.fail(»;
ofs.close();
assert(ofs.fail() && ofs.rdbuf()->close() == 0);
II test output file operations
ofs.clear(), ofs.open(tn, ios::out I ios::trunc);
assert(ofs.is_open() && ofs.rdbuf()->is_open(»;
ofs « "this is a test" « endl;
ofs.close();
assert(!ofs.is_open() && ofs.good(»;
assert (ofs.rdbuf()->open(tb, ios::app I ios::out) 1= 0);
ofs « "this is only a test n « endl;
ofs.close();
assert(!ofs.is_open() && ofs.good(»;
II test input file operations
char buf[SO];
ifs.c1ear(), ifs.open(tn, ios::in);
assert (ifs.is_open() && ifs.rdbuf()->is_open(»;
ifs.getline(buf, sizeof (buf»;
assert (strcmp(buf, "this is a test") == 0);
streampos pi = ifs.rdbuf()->pubseekoff(O, ios::cur);
ifs.getline(buf, sizeof (buf»;
assert (strcmp(buf, "this is only a test") 0);
assert(ifs.rdbuf()->pubseekpos(pi) == pi);
ifs.getline(buf, sizeof (buf»;
assert (strcmp(buf, "this is only a testh) 0);
ifs.rdbuf()->pubseekoff(O, ios::beg);
ifs.getline(buf, sizeof (buf»;
assert (strcmp(buf, "this is a test") == 0);
ifs.close();
assert(!ifs.is_open() && ifs.good(»;
II test combined file operations
ifstream nifs(tn, ios::in I ios::out);
ostream nofs(nifs.rdbuf(»;
assert (nifs.is_open() && nifs.good() && nofs.good(»;
nifs.rdbuf()->pubseekoff(O, ios::end);
nofs « "this is still just a test" « endl;
nifs.rdbuf()->pubseekoff(O, ios::beg);
nifs.getline(buf, sizeof (buf»;
<fstream> 335
Continuing assert (strcmp(buf, "this is a test"} ==a};
nifs.getline(buf, sizeof (buf});
tfstream..c
assert (strcmp(buf, "this is only a test .. } ==
O};
Part 2 of 2 nifs.getline(buf, sizeof (buf});
assert (strcmp(buf, "this is still just a test l1 } ==
O};
nifs.close(};
ofstream nnofs(tn, ios::in I ios::out I ios::ate};
assert(nnofs.is_open(}};
nnofs « "one last test" « endl;
nnofs.close(};
II test stdiobuf operations
F:ILE *fi = fopen(tn, IIr+"};
istdiostream istd(fi};
ostdiostream ostd(fi};
assert(fi != O};
assert (istd.buffered() ==0
&& istd.rdbuf(}->buffered(} == a};
istd.rdbuf(}->buffered(O}, istd.buffered(i};
assert(istd.buffered(} 1= 0
&& istd.rdbuf(}->buffered(} 1= 0);
assert (ostd.buffered() ==0
&& ostd.rdbuf(}->buffered(} 0);
ostd.rdbuf(}->buffered(O}, ostd.buffered(i);
assert (ostd.buffered() 1= 0
&& ostd.rdbuf(}->buffered(} 1= 0);
istd.getline(buf, sizeof (buf});
assert (strcmp(buf, IIthis is a test ll ) == O};
pi = istd.rdbuf()->pubseekoff(O, ios::end);
ostd « IIstill one more last test ll « endl;
assert(ostd.rdbuf()->pubseekpos(pi) ==pi);
istd.getline(buf, sizeof (buf»;
assert (strcmp(buf, "still one more last test") 0);
assert (fclose(fi) == 0 && remove(tn) == 0);
cout « IISUCCESS testing <fstream>" « endl;
return (0);
o
Testing <fstream>
tfstream. c Figure 13.9 shows the file tfstream.c. It tests the basic properties of the
classes defined in <fstream>. It does so by manipulating a temporary file
whose name is obtained by calling tmpnam, declared in <stdio .h>. First it
tries to write the file, then read from it, then intermix reads and writes. It
also performs some modest file-positioning operations along the wa~
Finall)T, it repeats a few of these operations using the classes istdiostream
and ostdiostream.
If all goes well, the program prints:
SUCCESS testing <fstream>
and takes a normal exit. It also removes the temporary file it created.
336 Chapter 13
Exercises
Exercise 13.1 Is there any difference between opening a file by name twice and opening
it once, then associating two istream or ostream objects with the control-
ling stream buffer?
Exercise 13.2 What happens if you manipulate multiple stdiobuf objects all associated
with the same FILE object? Can you make the behavior predictable?
Exercise 13.3 What happens if you associate an ostdiostream object with stdin?
Exercise 13.4 Why does filebuf: :close call filebuf: :_Init? (See the source file
filebuf.c on page 330.)
Exercise 13.5 Measure the performance of file reads and writes using this implementa-
tion of <fstream> and one that comes with a C++ compiler. If you can, also
measure the performance of filebuf. c translated with and without the
macro _HAS_PJP_CLIB defined.
Exercise 13.6 The function ungetc, declared in <stdio. h>, sometimes pushes characters
onto a stack within the FILE object. While this stack is not empt}r, the input
buffer is made to look empty so that getchar always calls the function
fgetc. Should an implementation ofclass f ilebuf that points inside a FILE
object point into this push-back stack? If so, describe how the stack must
be implemented to support proper extractions. If not, why not?
Exercise 13.7 Modify the functions defined in filebuf .c, as needed, to support auto-
matic switching between reading and writing. If, for example, a read fails,
the function should perform an innocuous stream-positioning operation,
then try the read again. Are there any circumstances where this added
functionality can also impact performance?
Exercise 13.8 [Harder] Implement the classes defined in <fstream> without making use
of the Standard C libraI)T. How do you deal with unbuffered operation of
stdiobuf objects? How portable is the code?
Exercise 13.9 [Very hard] Implement a portable method for positioning arbitrary files as
if they were sequences of bytes, so that you can perform arithmetic freely
(and successfully) on streampos objects.
•
Chapter 14: <1ostream>
Background
The header <iostream> declares four objects:
cin - cin, to control the unbuffered standard input stream (as does stdin)
cout • cout, to control the unbuffered standard output stream (as does stdout)
cerr - cerr, to control the unbuffered standard error stream (as does stderr)
clog - clog, to control the buffered standard error stream (as does stderr)
These objects are constructed for you, so they offer a particularly easy way
to perform input and output in a C++ program. The three objects that are
unbuffered also support reads and writes that are properly synchronized
with any C file operations that the program may also perform on the
standard streams. (See the previous chapter for a more precise description
of buffered and unbuffered operations on stream buffers.)
The header <iostream> is in many ways the culmination of all the
iostreams machinery that has gone before in this book. Historicall)', in fact,
the header <iostream.h> includes everything represented here in the
headers <ios>, <streambuf>, <istrea.m>, <ostream>, and <iostream>.
The general assumption has been that, if you want to work with any part
of iostreams, you certainly want to declare cin and friends as well.
The four objects declared in <iostream> also share a peculiar property.
They are all constructed before any expression that accesses them, even if
that expression is in a constructor for a static object. Equally, none of these
objects is destroyed, at least until after the execution of the last expression
that accesses them, even if that expression is in a destructor for a static
object. That means you can read and write the standard streams before main
is called and after main returns. At the very least, such latitude is very
convenient for debugging.
Pulling it off takes a bit of trickeT)', however. Consider the problem.
Unless the Standard c++ library indulges in some extra-linguistic magic,
<iostream> must contain declarations something like:
extern istream cin;
extern ostream cout;
extern ostream cerr;
extern ostream clog
338 Chapter 14
One of the object modules linked to form a program must define these
objects, if any other object lnodule refers to them. Say a constructor for the
static object x extracts characters from cin. Then cin must be constructed
before x is. But C++ defines no ordering among object modules for the
calling of constructors for static objects. The language cannot guarantee that
the two objects are constructed in the proper order.
Fortunately, the language does define the ordering of constructor calls
within a given object module. It promises to construct objects in the order
in which they appear in the translation unit that defines the object module.
You can't define cin early in each translation unit - exactly one instance
of the object must occur throughout the entire program. But you can define
something early enough to give you a fighting chance.
Init That sometllillg is an object of class ioe: : Init. I promised way back in
Chapter 6: <ios> that I'd return to this nested class, and at last I have. An
important part of the trick to constructing cin and friends is an additional
declaration in <iostream>. It looks something like:
static ios::Init _Ios_init;
This declares an object with the secret name _Ios_ini t, local to the
translation unit. Because the name doesn't have external linkage, each
translation unit that includes <iostream> declares a separate instance of
the object. Because class ioe:: Init defines a default constructor, that
constructor gets called to initialize the object. And because you must
include a header before you make any reference to anytIling it declares or
defines, that default constructor gets called in each translation unit before
any static constructor call execute that references cin and friends.
placement So far so good. A mechanism exists to get control in time. Presumabl~ it
new can ensure that cin aIld friends are properly constructed. For that, you can
use a placelnent new expression. (See page 83.) Sa~ for example, you want
to construct cin with the single-argument constructor that specifies a
pointer to the stream buffer fin. You can then write:
new (&cin) istream(&fin);
The effect is as if you llad originally defined cin by writing:
istream cin(&fin);
empty But this raises another problem. The definition for cin is also going to
constructor call a constructor. Either the definition will occur before the placement new
expression executes or after it:
• Say the definition occurs first. If it does anything nontrivial - such as
allocating storage and storing a pointer to it in the constructed object -
the placement new is going to stomp allover the result. This is bad
hygiene.
• Say the placement new expression occurs first. If the definition later
stores much anything at all in the object, it will Iness up the effect of the
placement new. This is disastrous.
<iostream> 339
The only scenario that works is to have the definition call a constructor that
does nothing at all. Then it doesn't matter whether the definition occurs
before or after. So long as the placement new expression does any necessary
initializing before the first use of the object, everything works fine. Classes
ios, istream, ostream, and who knows what else must each have a
constructor that does nothing. For much the same reason, each of these
classes must also have an empty (virtual) destructor. Otherwise, a critical
object might be destroyed before it is accessed from the destructor for
another static object.
nifty The final trick is to avoid constructing cin and friends too many times.
counter It's bad enough to have to double construct so many important objects within
the iostreams package. You certainly don't want to construct them all once
for each translation unit in a program that includes <iostream>. Class
ios : : J:nit again comes to the rescue by defining a static member object
for counting constructor calls, as in:
static int _J:nit_cnt;
Such a member object is shared across all objects of class ios: : J:nit. Say
the member object is initialized to -1, and that the constructor for
ios: : J:nit increments this common counter each time it is called. Then a
given call to the constructor knows whether it is the first such call. The
counter increments to zero only on that first call. And that is the call that
does the actual construction of cin and friends (and any support objects).
This trick is known as the "nifty counter" technique. With just a little more
logic, the nifty counter also can tell when to flush all the output streams, in
preparation for program termination.
explicit So there's at least one way to ensure that cin and friends have such an
initialization extraordinarily useful lifetime. The trick is clever enough, but you might
still wonder why the draft C++ Standard spells out so much of the machin-
er)'. Why not leave it to each implementation to solve the problem in its
own way, under the hood as it were?
As it turns out, situations can arise where even all this trickery fails to
deliver. Say the function f is defined in one translation unit and is called
during the construction of a static object in another translation unit. If f
wants to extract from cin, it needs to be sure that cin has been properly
constructed, as described above. But calling a function in a translation unit
does not ensure that static constructors for that translation unit have first
been called. The nifty counter supplied by the header <iostream> may not
come through in time.
The fix is to explicitly initialize an ios : : J:nit object within f, as in:
void f()
{ II read during static construction
ios::J:nit not_otherwise_used;
<can now re:Eerence cin, etc>
}
If this mechanism were not spelled out in the draft c++ Standard, there
would be no way to solve the initialization problem portably:
340 Chapter 14
Footnotes:
Footnotes 109) Constructors and destructors for static objects can access these objects to
read input from stdin or write output to stdout or stderr.
Future Directions
wide-character For the last time, I note that a major change is the addition of wide-char-
streams acter streams. (See page 119.) The apparent effect on <ioetream> is to leave
it alone. If you want to control the standard streams as wide-character
streams, you must construct the appropriate objects yourself. Still, this
leaves open the effect of reading or writing the standard streams as wide-
character streams. Amendment 1 (18094) says that the first operation on a
C stream, standard or otherwise, makes it either "narrow oriented" or
"wide oriented." Operations of the other orientation subsequently fail.
Nothing in the draft C++ Standard, however, yet promises to honor this
protocol in the C++ definition of wide-character streams.
<iostream> 341
Using <iostream>
You include the header <iostream> to make use of any of the objects
cin, cout, cerr, or clog. These objects let you read and write the standard
streams available to any executing C++ program:
cin - Extract from cin to read from the standard input stream.
cout • Insert to cout to write to the standard output stream.
cerr • Insert to cerr to write to the standard error stream.
clog - Insert to clog to write to the standard error stream.
Insertions to cerr are flushed at the end of each inserter call. (The format
flag ios: :unitbuf is set.) It is the preferred destination for debugging or
error messages, lest the program crash before displaying useful output.
Insertions to clog are more likely to be buffered. It is the preferred desti-
nation for "logging" high volumes of trace information, because insertions
to it are less likely to degrade overall program performance.
_Ini t These objects are constructed before any constructor is called to initialize
a static object. On rare occasions, as described earlier in this chapter, you
may need to be sure that cin and friends are initialized even earlier. In that
case, be sure that program execution declares an object of class ios: : Ini t
before the first expression that references any of these objects.
redirection You can redirect any of these streams by suppling a replacement pointer
to streambuf. For example, you can alter cin to extract from the named
file input by writing:
II II
#include <fstream>
#include <iostream>
filebuf fbI
fb. open ( I I input II , ios:: in) ;
cinordbuf (&fb);
Do not redirect cerr this way, however, lest diagnostics go astray:
Implementing <iostream>
iostream Figure 14.1 shows the file iostream, which implements the header
<iostream>. It is one of those rare headers that is both short and overt.
Given the general description of <iostream> earlier in this chapter, you
should not be surprised at anything you see here.
iostream. c Figure 14.2 shows the file iostreamo c. Its primary business is to define
the four objects declared in <iostream>. Its primary source of complexity
is the need to double construct these and other objects, as I described earlier.
_Noinit I first described the type _uninitialized, and the value _Noinit, on
_Uninitialized page 132. A constructor that takes a single argument of type _Uninitial-
ized is expected to perform no initialization. A declaration with the single
argument value _Noinit calls this constructor. Along the way, I've shown
several classes that have constructors of this sort - ios, streambuf,
istream, ostream, and filebuf. Here is where they all come into play.
342 Chapter 14
Figure 14. 1: II iostream standard header
#ifndef _IOSTREAM_
iostream
#define _IOSTREAM_
#include <fstream>
II standard stream declarations
extern istream cin;
extern ostream cout;
extern ostream cerr;
extern ostream clog;
static ios::Init _Ios_init;
#endif o
/1 object declarations
int ios::lnit::_Init_cnt -1;=
static filebuf fin(ios::_Noinit);
static filebuf fout(ios::_Noinit);
static filebuf ferr(ios::_Noinit);
istream cin(ios::_Noinit);
ostream cout(ios::_Noinit);
ostream cerr(ios::_Noinit);
ostream clog(ios::_Noinit);
ios::lnit::lnit()
{ /1 initialize standard streams first time
if (0 <= _Init_cnt)
++_Init_cnt;
else
1/ initialize standard streams
new (&fin) filebuf(stdin);
new (&cin) istream(&fin);
cin.tie(&cout);
new (&fout) filebuf(stdout);
new (&cout) ostream(&fout);
new (&ferr) filebuf(stderr);
new (&cerr) ostream(&ferr);
cerr.tie(&cout);
cerr.setf(ios::unitbuf);
new (&clog) ostream(&ferr);
clog.tie(&cout);
Init_cnt = 1;
ios::lnit::-Init()
{ 1/ flush standard streams last time
if (--_Init_cnt 0)
{ 1/ flush standard streams
cout. flush ( ) ;
cerr.flush();
clog. flush ( ) ;
}
int main()
{ II test basic workings of iostream definitions
assert(cin.good() && cin.exceptions() == ios::goodbit
&& cin.flags() == (ios::skipws I ios::dec)
&& cin.precision() == 6 && cin.width() == 0
&& cin.fill() == ' ');
assert (cout.good() && cout.exceptions() == ios::goodbit
&& cout.flags() == (ios::skipws I ios::dec)
&& cout.precision() == 6 && cout.width() == 0
&& cout.fill() == ' ');
assert (cerr.good() && cerr.exceptions() == ios::goodbit
&& cerr.flags()
== (ios::skipws I ios::dec I ios::unitbuf)
&& cerr.precision() == 6 && cerr.width() == 0
&& cerr.fill() == ' ');
assert (clog.good() && clog.exceptions() == ios::goodbit
&& clog.flags() == (ios::skipws I ios::dec)
&& clog.precision() == 6 && clog.width() == 0
&& clog.fill() == ' ');
cout « "Can write on streams:" « endl;
cout « "\tcout n « endl;
cerr « n\tcerr" « endl;
clog « "\tclog" « endl;
assert(cout.good(»;
assert(cerr.good(»;
assert(clog.good(»;
cout « "SUCCESS testing <iostream>" « endl;
return (0);
D
Exercises
Exercise 14. 1 A constructor that apparently does nothing might still store into the con-
structed object a pointer to a table of virtual function pointers (to define its
"shape"). Does this cause problems with the double-construction scheme
used to initialize iostreams objects?
Exercise 14.2 What would be the constraints on using cin and friends if they were not
constructed in a special way?
Exercise 14.3 Implement the object cargs that delivers the command-line argument
strings one after the other. How do you delimit the arguments in the
stream?
Exercise 14.4 [Harder] Implement cout so that it writes text to a window on your favorite
windowing operating system.
Exercise 14.5 [Very hard] Implement cin and friends using no double construction.
Chapter 15: <string>
Background
One of the great strengths of C, from its earliest days, has been its ability
to manipulate sequences of characters. The null-terminated sequence, or
string, even has a bit of language support. You can write a string literal,
such as .. abc and the translator will construct for you a non-modifiable
II ,
Future Directions
c_str The member function string: :c_str is now string: : data.
put_at The member function string: : put_at no longer appends a character
when the character sequence is accessed exactly at its end.
basic_string The Committee has replaced class string with a template class called
basic_string. One instantiation, for elements of type char, reproduces
class string and related functions, as described in this chapter. Another
instantiation, for elements of type wchar_t, reproduces class wstring and
related functions, as described in the next chapter. This approach resembles
the new templates for the iostreams classes, but is independent.
358 Chapter 15
functional As with iostreams, the basic functionality of class string does not change
compatibility with the introduction of templates. A program written to match the Infor-
mal Review Draft should remain essentially unchanged. The code pre-
sented here is effectively an efficient specialization for type char.
Using <string>
You include the header <string> to make use of the class string.
Objects of this class let you manipulate in-memory character sequences that
vary dynamically in length. The string object allocates and frees storage
as needed to accommodate changes in length of the controlled sequence.
construction To construct a string object xO with an initially empty character se-
quence, you can simply write:
string xO;
length Each object x reports the length of its character sequence with the call
reserve x.length(). It also reports its reserve size with the call x. reserve (). For a
newly constructed string object, the reserve size suggests how much
storage to initially allocate for the character sequence. You can, for example,
specify an initial reserve size of 100 characters by writing:
capacity string xO(100, reserve);
For an object with a non-empty character sequence, the reserve size sug-
gests how much storage is currently available to hold the character se-
quence. Thus, xO • length () <= xO. reserve () at all times. As a rule, you
can ignore the reserve size. The implementation will guess it for you. (But
see the discussion on page 279.)
To construct a string object and define its initial character sequence,
you have a number of choices. In each case below, the string literal in the
comment shows the resulting character sequence. The implied null charac-
ter at the end of this string literal is not part of the character sequence. You
can write:
string xl(5, default_size); II "\0\0\0\0\0 11
string x2('a'); II nail
string x3('a', 5); II lIaaaaa"
string x4(nabcde ll ) ; II abcde "
11
Continuing _X.length(»); }
size_t find_last_not_of(const char *, size_t = NPOS,
string Part
size_t = NPOS) const;
30f3 size_t find_last_not_of(char _C, size_t P = NPOS) const
{return (find_last_not_of«const char *)&_C, _P, 1»;
string substr(size_t _P = 0, size_t _N = NPOS) const
{return (string(*this, _P, _N»; }
int compare(const string&, size_t = 0, size_t = NPOS) const;
int compare(const char *, size_t = 0, size_t = NPOS) const;
int compare(char, size_t = 0, size_t = 1) const;
private:
_Bool _Grow(size_t, _Bool = 0);
void _Tidy(_Bool = 0);
void _Xlen() const
{lengtherror(lIstring too longll).raise(); }
void _Xran() const
{outofrange(lIinvalid string position").raise();
char *_ptr;
size_t _Len, _Res;
};
inline string operator+(const string& _L, const string& _R)
{return (string(_L) += _R); }
inline string operator+(const char *_L, const string& _R)
{return (string(_L) += _R); }
inline string operator+(char _L, const string& _R)
{return (string(_L) += _R); }
inline string operator+(const string& _L, const char *_R)
{return (string (_L) += _R); }
inline string operator+(const string& _L, char _R)
(return (string(_L) += _R); }
inline _Boo1 operator==(const string& _L, const string& _R)
{return (_L.compare(_R) == 0); }
inline _Boo1 operator==(const char * _L, const string& _R)
{return (_R.compare(_L) == 0); }
inline _Boo1 operator==(char _L, const string& _R)
{return (_R.compare(_L) == 0); }
inline _Boo1 operator==(const string& _L, const char *_R)
{return (_L.compare(_R) == 0); }
in1ine _Bool operator==(const string& _L, char _R)
(return (_L.compare(_R) == 0); }
inline _Bool operatorl=(const string& _L, const string& _R)
(return (!(_L == _R»; }
inline _Bool operator!=(const char *_L, const string& _R)
{return (!(_L == _R»; }
inline _Bool operatorl=(char _L, const string& _R)
{return (!(_L == _R»; }
inline _Bool operatorJ=(const string& _L, const char *_R)
{return (!(_L == _R»; }
inline _Bool operator!=(const string& _L, char _R)
{return (!(_L == _R»; }
istream& operator»(istream&, string&);
istream& getline(istream&, string&, char = '\n');
inline ostream& operator«(ostream& _0, const string& _X)
{return (_O.write(_X.c_str(), _X.length(»); }
#endif 0
366 Chapter 15
Implementing <string>
string Figure 15.1 shows the file string, which implements the header
<string>. For a discussion of the macro _HAS_SIGNED_CHAR, see page 195.
The class string defines four secret protected member functions:
_Grow - _Grow, which alters the storage reserved for the character sequence
_Tidy • _Tidy, which initializes <_Tidy ( ) ) the member objects at construction
time or discards any character sequence LTidy( 1» and reinitializes the
member objects
_Xlen - _Xlen, which reports a length error
_Xran - _Xran, which reports a range error
For a discussion of the exceptions associated with length and range errors,
see Chapter 3: <exception>.
string.c Figure 15.2 shows the file string.c. It defines the member functions
_Grow and _Tidy, both of which are likely to be called for any object of class
string.
MIN_SIZE The macro MIN_SIZE is both a minimum size for character-sequence
storage and a minimum increment for adding more. The function _Grow
uses this parameter to round up requests for more storage, in the hopes of
minimizing reallocations. It also uses the parameter trim as an indication
that shrinking storage may be a good idea. The function is otherwise
reluctant to do so.
realloc Note that _Grow allocates, or reallocates, storage by calling realloc,
declared in <stdlib.h>. That function can sometimes adjust the size of
allocated storage more efficiently than the equivalent combination of new
and delete expressions. For a description of the function _Nomemory, see
page 48.
Many member functions for class string come in groups of three. For
members of such a group I chose source file names that have a common
prefix, followed by:
• c, for an argument that is a single character or a repetition of characters
- s, for an argument that is a null-terminated string or an array of charac-
ters of specified length
• x, for an argument that is a substring or the entire character sequence
controlled by a string object
Often, the three flavors are just different enough to profit from distinct
function definitions.
strasc. c As the first example, here are the three source files that define the
strass. c variations of the member function assign. Figure 15.3 shows the file
strasx. c strasc. c. Figure 15.4 shows the file strass. c. And Figure 15.5 shows the
file strasx. c. You can see the common approach. First validate the argu-
ments, then call_Grow to adjust storage size for the character sequence, then
determine the new character sequence. The last act is to store the new
sequence length in the member object _Len. As is often the case, the
<string> 367
Figure 15.2: II string -- string basic members
#include <stdlib.h>
string.c
#include <string>
return (*this);
o
than for the earlier trios, mostly because of the need to define both old and
new substrings. But it is otherwise much like what has gone before.
strget.e Figure 15.16 shows the file strget .e, which defines the member func-
strput.e tion get_at. And Figure 15.17 shows the file strput • e, which defines the
companion function put_at. Both are simple, but as I noted earlier, the
latter function has already been changed in the draft C++ Standard. It no
longer appends the character when _Len == pO.
372 Chapter 15
Figure 15.13: II strrec -- string::replace(size_t, size_t, char, size_t)
#include <string.h>
strrec.c #include <string>
return (NPOS);
o
return (NPOS);
o
strcopy.c Figure 15.18 shows the file strcopy.c, which defines the member
function copy. It too is simple.
For the "find" member functions (names beginning with find), I found
it sufficient to define the "string or array" versions (source files with names
ending in s) only. All others are inline functions that call these versions.
strfis.c Figure 15.19 shows the file strfis.c, which defines the string or array
strrfis.c version of the member function find. As an optimization, it first scans for
the initial character of the character sequence to match. The (mildly messy)
logic could otherwise be made simpler. Figure 15.20 shows the file
strrfis .c, which defines the string or array version of the member
function rfind. It has no handy library function to perform the equivalent
reverse scan, so its logic is perforce simpler (and possibly slower).
376 Chapter 15
Figure 15.23: 1/ strffns -- string::find_first_not_of(const char *, size_t,
1/ size_t)
strffns.c
#include <string.h>
#include <string>
return (NPOS);
o
strffs.c Figure 15.21 shows the file strffs.c, which defines the string or array
strfls.c version of the member function find_first_of. And Figure 15.22 shows
the file strfls. c, which defines the string or array version of the member
function find_last_of. Compare this pair with the pair that follows.
strffns.c Figure 15.23 shows the file strffns. c, which defines the string or array
strflns.c version of the member function find_first_not_of. And Figure 15.24
shows the file strflns .c, which defines the string or array version of the
member function find_last_not_of. All four functions are very similar.
strcoc.c The last set of three source files define the variations of the member
strcos.c function compare. Figure 15.25 shows the file strcoc. c. Figure 15.26 shows
strcox.c the file strcos . c. And Figure 15.27 shows the file strcox. c. The messiest
<string> 377
Figure 15.25: II strcoc -- string::compare(char, size_t, size_t)
#include <string>
strcoc.c
int string::compare(char ch, size_t pO, size_t ns) const
{ II compare a repeated char to a string
if (_Len < pO)
_Xran();
size_t n = _Len - pO;
for (const char *s _ptr + pO, *t s + (n < ns ? n ns);
s < t; ++s)
if (*s !=ch)
return (*(unsigned char *)s < (unsigned char)ch
? -1 +1);
return (n < ns ? -1 : n == ns ? 0 : +1);
o
part of the logic occurs when the shorter of two character sequences is a
prefix of the longer one, or when the character sequences are equal.
378 Chapter 15
Figure 15.28: 1/ strxstr -- operator»(istream&, string&)
#include <ctype.h>
strxstr.c #include <limits.h>
#include <string>
operator» The last two source files define tIle functions that extract character
getline sequences into string objects. Figure 15.28 shows the file strxstr.c,
which defines the string version of operator». And Figure 15.29 shows
the file strgline. c, which defines the string version of getline. These
differ only in small ways from the istream member functions that extract
character sequences. (See Chapter 8: <istream>.) They call the istream
member functions differentl)T, naturally enough. And they handle excep-
tions using the macro _CATCH_I 0_ (x) instead of _CATCH_IO_END. (See page
128.) The overall logic is otherwise quite similar.
Testing <string>
tstring. c Figure 15.30 shows the file tstring. c. It tests the basic properties of the
class string defined in <string>. It does so by testing the various string
operations in related groups, at least for their minimum functionalit)r.
If all goes well, the program prints:
SUCCESS testing <string>
and takes a normal exit.
<string> 379
Figure 15.29: II strgline -- getline{istream&, string&, char)
#include <string>
strgline.c
istream& getline(istream& is, string& str, char delim)
{ II get to string up through delimiter or count
size_t n = 0;
str.remove{);
_Bool copied = 0;
_TRY_IO_BEGIN
if (is.ipfx(l»
for (int ch; ; )
if (NPOS - 1 <= n)
{ II record count failure and quit
is.setstate(ios::failbit);
break;
}
else if ({ch = is.rdbuf{)->sbumpc{» == EOF)
{ II record eof and quit
is.setstate(ios::eofbit);
break;
}
else
{ II count it and test for delim
if (ch == delim)
break;
str.append{(char)ch), ++n, copied 1;
}
if (lcopied)
is.setstate{ios::failbit);
is.isfx{);
_CATCH_IO_{is);
return (is);
o
Exercises
Exercise 15.1 Write a function that reverses the characters in a sequence controlled by a
string object.
Exercise 15.2 Describe all the operations performed by functions declared in
< string. h> and how to do the same thing using only functions declared
I
in string.
Exercise 15.3 Is it permissible to define an array of NPOS characters?
Exercise 15.4 Why do you think the "find" functions return NPOS when an argument is
sill~ rather than throw an exception?
Exercise 15.5 [Harder] Implement class string in such a way that a character sequence
is actually copied only when two or more string objects designate the
same sequence and one attempts to alter it.
Exercise 15.6 [Very hard] Define and implement a string class that has no limit on the
length of a character sequence.
380 Chapter 15
Figure 15.30: II test <string>
#include <assert.h>
tstring.c
#include <string.h>
Part 1 of 3 #include <string>
#include <iostream>
#include <strstream>
int main ( )
( II test basic workings of string definitions
string sl, 82(30, reserve), 83(4, default_size);
string S4(1I84 11 ), Ss(ISsXXX", 2), s6('a', 3);
string s7«unsigned char)'b', 5), s8{(signed char)'c');
sl.reserve(20);
assert (sl.reserve() == 20 && s2.reserve() 30);
assert(strcmp(sl.c_str(), 1111) == 0);
assert(strcmp(s2.c_str(), 1111) == 0);
assert(memcmp(s3.c_str(), 11\0\0\0\0", 5) 0);
assert(strcmp(s4.c_str(), Is4") == 0);
assert(strcmp(ss.c_str(), "Ss") == 0);
assert(strcmp(s6.c_str(), "aaa ll ) == 0);
assert(strcmp(s7.c_str(), IIbbbbb") == 0);
assert(strcmp(s8.c_str(), "C") == 0);
II test assignments
sl "hello", assert (strcmp (81. c_str (), "hello") 0) ;
sl 'x', assert(strcmp(sl.c_str(), "x") == 0);
sl s4, assert(strcmp(sl.c_str(), "s4 11 ) == 0);
sl.assign(IIAB"), assert(strcmp(sl.c_str(), "AB") == 0);
sl.assign('C'), assert(strcmp(sl.c_str(), "cn) 0);
sl.assign(s4), assert(strcmp(sl.c_str(), IIs4") == 0);
II test appends
sl += "abc", assert(strcmp(sl.c_str(), Is4abc") == 0);
sl += 'd', assert(strcmp(sl.c_str(), "s4abcd ll ) == 0);
sl += s4, assert(strcmp(sl.c_str(), IIs4abcds4") == 0);
s 1 = II A", s 1. append ( II BC II ) ;
assert(strcmp(sl.c_8tr(), "ABC") == 0);
sl.append('D'), assert(strcmp(sl.c_str(), "ABCD") ==
0);
sl.append(s4), assert(strcmp(sl.c_str(), IIABCDs4") == 0);
assert(strcmp«s4 + ss).c_str(), "s4ss") == 0);
assert(strcmp«s4 + "ss").c_str(), Is4ss") == 0);
assert(strcmp«"s4 11 + ss).c_str(), Is4ss") == 0);
assert(strcmp«s4 + 's').c_str(), "s4s") 0);
assert(strcmp«'4' + ss).c_str(), "4ss lI ) == 0);
II test inserts
sl = II abc II ;
sl.insert(3, "Dd").insert(l, "BC", l).insert(O, "A");
assert(strcmp(sl.c_str(), "AaBbcDd") == 0);
s 1. insert (7, , E', 2). insert (4, , C' ) ;
assert(strcmp(sl.c_str(), "AaBbCcDdEE") == 0);
sl.insert(10, s4).insert(0, s4, l).insert(O, s4, 0, 1);
assert(strcmp(sl.c_str(), "s4AaBbCcDdEEs4") == 0);
II test removes
sl.remove(O, 2);
assert(strcmp(sl.c_str(), IIAaBbCcDdEEs4 11 ) == 0);
sl.remove(8), assert(strcmp(sl.c_str(), "AaBbCcDd") 0);
sl.remove(), assert{strcmp(sl.c_str(), 1111) == 0);
<string> 381
Background
wide A significant addition to Standard C (15090) is support for wide charac-
characters ters. In Standard C, type wchar_t is defined in <stddef. h> (among other
places) as a synonym for one of the integer types. It can represent all the
distinct codes of a large character set, otherwise known as wide characters.
Hence, it is the internal representation of choice for manipulating text in
languages such as Chinese and Japanese. While still a specialized style of
programming, such "international" capabilities are rapidly growing in
importance, for obvious commercial reasons.
Support for wide characters in Standard C, as originally specified, is
minimal. The header <stdlib.h> defines a handful of functions for con-
verting between wide characters and multibyte characters - sequences of
one or more bytes that represent the same character set for external trans-
mission. Otherwise, a program that manipulates wide characters has to
supply its own utility functions.
Amendment 1 Amendment 1 to the C Standard (15094) changed all that. Standard C
now includes a rich assortment of wide-character functions. You'll find
analogs for all the facilities of <ctype. h> in <wctype. h>, and then some.
You'll find analogs for most of the facilities of <stdio eh>, and all those in
<string. h> in <wchar. h>. Put simpl)!, you can convert large quantities of
I
Footnotes:
Footnotes 112) The function does not append a null wide character to the string.
Future Directions
c_wcs The member function wstring: : c_wcs is now wstring: : data.
put_at. The member function wstring: : put_at no longer appends a wide
character when the sequence is accessed exactly at its end.
basic_string As I mentioned in the previous chapter, the Committee has replaced class
wstring with a template class called basic_string. One instantiation, for
elements of type char, reproduces class string and related functions, as
described in the previous chapter. Another instantiation, for elements of
typewchar_t, reproduces class ws tring and related functions, as described
in this chapter. (See the discussion on page 357.) As before, the code
presented here is effectively an efficient specialization for type wchar_t.
<wstring> 395
Using <wstring>
The description that follows closely resembles the one for the header
<wstring> in the previous chapter. I present it here to keep the discussion
of class wstring localized. Also, there are just enough differences to remem-
ber that interpolating from the description of class string can mislead.
You include the header <wstring> to make use of the class wstring.
Objects of this class let you manipulate in-memory wide-character se-
quences that vary dynamically in length. The wstring object allocates and
frees storage as needed to accommodate changes in length of the controlled
sequence.
construction To construct a wstring object xO with an initially empty wide-character
sequence, you can simply write:
wstring xO;
length Each wstring x reports the length of its wide-character sequence with the
reserve call x • length ( ). It also reports its reserve size with the call x. reserve () .
For a newly constructed wstring object, the reserve size suggests how
much storage to initially allocate for the wide-character sequence. You can,
for example, specify an initial reserve size of 100 wide characters by writing:
capacity wstring xO(100, reserve);
For an object with a non-empty wide-character sequence, the reserve size
suggests how much storage is currently available to hold the wide-charac-
ter sequence. Thus, xO • length () <= xO. reserve ( ) at all times. As a rule,
you can ignore the reserve size. The implementation will guess it for you.
(But see the discussion on page 279.)
To construct a wstring object and define its initial wide-character se-
quence, you have a number of choices. In each case below, the wide string
literal in the comment shows the resulting wide-character sequence. The
implied null wide character at the end of this wide string literal is not part
of the wide-character sequence. You can write:
wstring x1(5, default_size); II LII\O\O\O\O\OIl
wstring x2 ('a'); / I Llla"
wstring x3('a', 5); II L"aaaaa"
wstring x4 (llabcde"); I I Lllabcde"
wstring x5 ( II abcdefg II , 5); I I Lllabcde II
sUbstrings You can also define the initial wide-character sequence when you con-
struct a wstring object by selecting a substring from another wstring
object. Given the definition of x5 above, you can write:
wstring x6(x5); II L"abcde ll
wstring x7(x5, 1); II L"bcde"
wstring x8(x5, 1, 3); II Lllbcd ll
A substring thus has two default arguments. In order, these are:
• the initial position pos in the wide-character sequence, counting from
zero (default is zero)
• the maximum number of wide characters n to include from the remain-
der of the wide-character sequence (default is NPOS, a huge value)
396 Chapter 16
notation Class wstring supports numerous combinations of argument types for
practically every function that constructs or manipulates such objects. For
brevit)r, I define here a terse and uniform style for writing various argument
combinations. The resultant argument is:
• a single wide-character, with the argument c
• a repetition of wide characters, with the arguments e, rep
• a null-terminated array of wide characters, with the argument s
• an array of wide characters with a specified length, with the arguments
s, n
• a substring, with the arguments x, or x, pos, or x, pos, n, as described
above
With this notation, I can more quickly summarize all the ways you can
perform various operations involving wstring objects. Here, for example,
is a summary of most of the constructors I showed above:
wstring x2(c);
wstring x3(e, rep);
wstring x4(s);
wstring x5(s, n);
wstring x6 (x) ;
wstring x7(x, pos);
wstring x8(x, pos, n);
A number of member functions alter the wide-character sequence con-
trolled by a wstring object xO.
To assign a new wide-character sequence, in place of any existing one:
assign xO.assign(e) xO =e
xO.assign(e, rep)
xO.assign(s) xO =s
xO.assign(s, n)
xO.assign(x) xO =x
xO.assign(x, pos)
xO.assign(x, pos, n)
Implementing <wstring>
This implementation of <wstring> closely parallels that for <string>,
as you migilt expect. To maximize that parallelisln, I make extensive use of
the functions declared in <wehar. h>. Nate tilat these are not yet available
in many implementations of the Stalldard C library: (See AppendiX A:
Interfaces.)
wstring Figure 16.1 SilOWS the file wstring, which iInplemellts tIle header
<wstring>. I defined the static member object _Nullwes as a handy source
of a null wide character. (Not all implementations support wide literals.)
The class wstring defilles also four secret protected member functions:
400 Chapter 16
Figure 16. 1: II wstring standard header
wstring #ifndef _WSTRING_
#define _WSTRING_
Part 1 of 3 #include <exception>
II class wstring
class wstring {
public:
static const wchar_t _Nullwcs[];
wstring( )
{_Tidy ();
wstring(size_t _N, capacity _C)
{_Tidy(), _Res = _N;
if (_C == default_size)
assign«wchar_t)O, _N); }
wstring(const wstring& _x, size_t _P 0, size_t _N NPOS)
{_Tidy(), assign(_X, _P, _N); }
wstring(const wchar_t *_S, size_t _N NPOS)
{_Tidy(), assign(_S, _N); }
wstring(wchar_t _C, size_t _N = 1)
{_Tidy(), assign(_C, _N); }
-wstring( )
{_Tidy(l); }
wstring& operator=(const wstring& _X)
{return (assign(_X»; }
wstring& operator=(const wchar_t *_S)
{return (assign(_S»; }
wstring& operator=(wchar_t _C)
{return (assign(_C»; }
wstring& operator+=(const wstring& _X)
{return (append(_X»; }
wstring& operator+=(const wchar_t *_S)
{return (append(_S»; }
wstring& operator+=(wchar_t _C)
{return (append(_C»; }
wstring& append(const wstring&, size_t = 0, size_t NPOS);
wstring& append(const wchar_t *, size_t = NPOS);
wstring& append(wchar_t, size_t = 1);
wstring& assign(const wstring&, size_t = 0, size_t NPOS);
wstring& assign(const wchar_t *, size_t = NPOS);
wstring& assign(wchar_t, size_t = 1);
wstring& insert(size_t, const wstring&, size_t = 0,
size_t = NPOS)i
wstring& insert(size_t, const wchar_t *, size_t = NPOS);
wstring& insert(size_t, wchar_t, size_t = 1);
wstring& remove(size_t = 0, size_t = NPOS);
wstring& replace(size_t, size_t, const wstring&,
size_t = 0, size_t = NPOS)i
wstring& replace(size_t, size_t, const wchar_t *,
size_t = NPOS)i
wstring& replace(size_t, size_t, wchar_t, size_t 1);
wchar_t get_at (size_t) const;
void put_at(size_t, wchar_t);
wchar_t operator[] (size_t _N) const
{return (_Ptr[_N]); }
wchar_t& operator[] (size_t _N)
<wstring> 401
_Grow II _Grow, which alters the storage reserved for the wide-character sequence
_Tidy iii _Tidy, which illitializes <_Tidy () ) tIle member objects at construction
time or discards allY wide-character sequence LTidy (1) ) and reinitial-
izes the nlember objects
<wstring> 403
Figure 16.2: II wstring -- wstring basic members
#include <stdlib.h>
wstring.c #include <wstring>
31; II 2 A N - 1
wstrasc.c As the first example, here are the three source files that define the
wstrass.c variations of the member function assign. Figure 16.3 shows the file
wstrasx.c wstrasc . c. Figure 16.4 shows the file wstrass. c. And Figure 16.5 shows
the file wstrasx. c. You can see the cornmon approach. First validate the
arguments, then call_Grow to adjust storage size for the character sequence,
then determine the new character sequence. The last act is to store the new
408 Chapter 16
Figure 16. 12: II wstrrem -- wstring::remove{size_t, size_t)
#include <wchar.h>
wstrrem.c #include <wstring>
return (*this);
o
sequence length in the member object _Len. As is often the case, the
substring version requires special handling because the substring may be
controlled by the same object.
<wchar.h> You can probably guess the behavior of the various wide-character
functions declared in <wchar. h>, even if you are not yet familiar with this
newer header. Functions ,vhose names begin with wcs are direct analogs of
those declared in <string.h> whose names begin with str. Similarly,
functions whose names begin with wmem are direct analogs of those whose
names begin with mem.
wstrapc.c Anotller set of three source files define the variations of the member
wstraps.c function append. Figure 16.6 shows the file wstrapc. c. Figure 16.7 shows
wstrapx.c the file wstraps .c. And Figure 16.8 shows the file wstrapx.c. All follow
much the same pattern as tIle previous trio. In this case, however, append-
ing part or all of a substring to itself requires little additional caution.
wstrinc.c Three more source files define the variations of the member function
wstrins.c insert. Figure 16.9 shows the file wstrinc .c. Figure 16.10 shows the file
wstrinx.c wstrins.c. And Figure 16.11 shows the file wstrinx.c. Here the special
precaution is to sometimes call wmemmove instead of wmem.cpy, both of which
are declared in <wchar. h>. The former works properly even when copying
a wide-character sequence to elements that overlap the original ones.
wstrrem.c Figure 16.12 shows the file wstrrem. c, which implements the member
function remove. It too calls wmemmove to close up any hole made in the
character sequence.
wstrrec.c Three source files define tIle variations of the member function replace.
wstrres.c Figure 16.13 shows the file wstrrec.c. Figure 16.14 shows the file
wstrrex.c wstrres.c. And Figure 16.15 shows the file wstrrex.c. The logic is a bit
more complex than for the earlier trios, mostly because of the need to define
<wstring> 409
Figure 16. 13: II wstrrec -- wstring::replace(size_t, size_t, wchar_t, size_t}
wstrrec.c #include <wchar.h>
#include <wstring>
return (NPOS);
o
both old and new substrings. ~t is otherwise much like what has gone
before.
wstrget.c Figure 16.16 shows the file wstrget.c, which defines the member
wstrput.c function get_at. And Figure 16.17 shows the filewstrput. c, which defines
the companion function put_at. Both are simple, but as I noted earlier, the
latter function has already been changed in the draft c++ Standard. It no
longer appends the character when _Len == pO.
wstrcopy.c Figure 16.18 shows the file wstrcopy.c, which defines the member
function copy. It too is simple.
For the "find" member functions (names beginning with find), I found
it sufficient to define the "wide string or array" versions (source files with
names ending in s) only. All others are inline functions that call these
versions.
412 Chapter 16
Figure 16.20: II wstrrfis -- wstring::rfind(const wchar_t *, size_t, size_t)
#include <wchar.h>
wstrrfis.c #include <wstring>
return (NPOS);
o
wstrfis.e Figure 16.19 shows the filewstrfis .e, which defines the wide string or
wstrrfis.e array version of the member function find. As an optimization, it first scans
for the initial character of the wide character sequence to match. The (mildly
messy) logic could otherwise be made simpler. Figure 16.20 shows the file
wstrrfis. e, which defines the wide string or array version of the member
function rfind. It has no handy library function to perform the equivalent
reverse scan, so its logic is perforce simpler (and possibly slower).
wstrffs.c Figure 16.21 shows the file wstrffs. e, which defines the wide string or
wstrfls.c array version of the member function find_first_of. And Figure 16.22
shows the file wstrfls. c, which defines the wide string or array version
of the member function find_last_of. Compare this pair with the pair
that follows.
<wstring> 413
return (NPOS);
o
wstrffns.c Figure 16.23 shows the file wstrffns .c, which defines the wide string
wstrflns.c or array version of the member function find_first_not_of. And Figure
16.24 shows the file wstrflns. c, which defines the wide string or array
version of the member function find_last_not_of. All four functions are
very similar.
wstrcoc.c The last set of three source files define the variations of the member
wstrcos.c function compare. Figure 16.25 shows the file wstrcoc. c. Figure 16.26
wstrcox.c shows the file wstrcos. c. And Figure 16.27 shows the file wstrcox. c. The
messiest part of the logic occurs when the shorter of two ''\Tide-character
sequences is a prefix of the longer one, or when the wide-character se-
quences are equal.
414 Chapter 16
int wstring:: compare (const wstring& str, size.._t pO, si ze_t ns)
const
{ II compare a substring to a wstring
if (_Len < pO)
_Xran();
size_t n = _Len - pO;
if (str.length() < ns)
ns = str.length();
size_t ans = wmemcmp <. ptr ... pO, str. c_wcs ( ) ,
n < ns ? n : ns);
return (ans != 0 ? ans : n < ns ? -1 : n == ns ? 0 +1);
o
Testing <wstring>
twstring. c Figure 16.28 shows the file twstring. c. It tests the basic properties of
tile class wstring defined in <wstring>. It does so by testing the various
string operations in related groups, at least for their minimum functionality.
This source file is long because I avoided the use of wide string literals.
Otherwise, it is much the same as tstring. c, in the previolls chapter.
If all goes well, the program prints:
SUCCESS testing <wstring>
and takes a normal exit.
Exercises
Exercise 16. 1 Typewchar_t can have a signed integer representation. What effect can this
have on comparison of two wstring objects? What effect can it have on
how you compate two null-terminated wide-character strings?
Exercise 16.2 What other elemellt type, besides char or wchar_t, makes a sensible basis
for a string class?
Exercise 16.3 Describe all the operations performed by functiolls declared in <wchar. h>,
and how to do the same thing using only functions declared in wstring.
Exercise 16.4 Define the template class basic_string that behaves the same as class
string, for the element type char, and class wstring, for the element type
wchar_t. Do you need any template parameters besides the element type?
Exercise 16.5 [Harder] Implement an mbstring class that controls a multibyte character
sequence. What functions do you define for converting to and from wide-
character codes? How do you COtlnt multibyte characters?
Exercise 16.6 [Very hard] Define and implement a text class that YOll can access as either
multibyte or wide-character sequences.
416 Chapter 16
Figure 16.28: II test <wstring>
#include <assert.h>
twstring.c #include <wchar.h>
Part 1 of 4 #include <wstring>
#include <iostream>
#include <strstream>
int main ( )
{ II test basic workings of wstring definitions
wstring sl, s2(30, reserve), s3(4, default_size);
wstring s4(Ls4), sS(LsSxxx, 2), s6(_L('a'), 3);
wstring s8(_L('c'»;
sl.reserve(20);
assert (sl.reserve() == 20 && s2.reserve() 30);
assert(wcscmp(sl.c_wcs(), L_4) == 0);
assert(wcscmp(s2.c_wcs(), L_4) 0); ==
assert(wmemcmp(s3.c_wcs(), L_4, S) == 0);
assert(wcscmp(s4.c_wcs(), Ls4) 0); ==
assert(wcscmp(sS.c_wcs(), LsS) 0); ==
assert(wcscmp(s6.c_wcs(), Laaa) == 0);
assert(wcscmp(s8.c_wcs(), Lc) == 0);
II test assignments
sl Lhello, assert(wcscmp(sl.c_wcs(), Lhello) 0); ==
sl _L('x'), assert(wcscmp(sl.c_wcs(), Lx) == 0);
sl s4, assert(wcscmp(sl.c_wcs(), Ls4) 0); ==
sl.assign(LAB), assert(wcscmp(sl.c_wcs(), LAB) 0); ==
sl.assign(_L('C'», assert(wcscmp(sl.c_wcs(), LC) 0); ==
sl.assign(s4), assert(wcscmp(sl.c_wcs(), Ls4) 0);
II test appends
sl += Labc, assert(wcscmp(sl.c_wcs(), Ls4abc) 0); ==
sl += _L('d'), assert(wcscmp(sl.c_wcs(), Ls4abcd) 0);
sl += s4, assert(wcscmp(sl.c_wcs(), Ls4abcds4) == 0);
sl = LA, sl.append(LBC);
assert(wcscmp(sl.c_wcs(), LABC) == 0);
sl.append(_L('D'», assert(wcscmp(sl.c_wcs(), LABCD) 0); ==
sl.append(s4), assert(wcscmp(sl.c_wcs(), LABCDs4) 0);
assert(wcscmp«s4 + sS).c_wcs(), Ls4sS) == 0);
assert(wcscmp«s4 + LsS).c_wcs(), Ls4sS) 0); ==
assert(wcscmp«Ls4 + sS).c_wcs(), Ls4sS) 0); ==
assert(wcscmp«s4 + _L('S'».c_wcs(), Ls4S) 0);
assert(wcscmp«_L('4') + sS).c_wcs(), L4sS) 0); ==
II test inserts
418 Chapter 16
Continuing sl = Labe, sl.insert(3, LDd).insert(l, LBC, l).insert(O, LA);
assert(wescmp(sl.c_wcs(), LAaBbcDd) == 0);
twstring.c
sl.insert(7, _L('E'), 2).insert(4, _L('C'»;
Part 3 of 4 assert(wescmp(sl.c_wcs(), LAaBbCcDdEE) == 0);
sl.insert(10, s4).insert(0, s4, l).insert(O, s4, 0, 1);
assert(wescmp(sl.c_wcs(), Ls4AaBbCcDdEEs4) == 0);
II test removes
sl.remove(O, 2);
assert(wescmp(sl.c_wcs(), LAaBbCcDdEEs4) == 0);
sl.remove(8), assert(wcscmp(sl.c_wcs(), LAaBbCcDd) 0);
sl.remove(), assert(wcscmp(sl.c_wcs(), L_4) == 0);
II test replace
sl.replaee(O, 0, L123ab789), sl.replace(3, 2, L45678, 3);
assert(wescmp(sl.c_wcs(), L123456789) == 0);
sl.replaee(l, 3, _L('x'), 2).replace(0, 0, _L('O'»;
assert(wcscmp(sl.c_wcs(), LOlxx56789) == 0);
sl.replaee(3, 1, s4, 1).replace(2, 1, s4);
assert(wescmp(sl.c_wcs(), LOls4456789) == 0);
II test accesses
sl.put_at(2, _L('2'»;
assert (sl.get_at(l) == _L('l') && sl[2] _L('2'»;
assert«sl[3] = _L('3'» == _L('3'»;
assert(sl.length() == 10);
sl.resize(3), assert(wescmp(sl.c_wcs(), L012) 0);
sl.resize(5, _L('X'»;
assert(wcscmp(sl.c_wcs(), L012XX_) == 0);
sl.resize(6), assert(wmemcmp(sl.c_wcs(), L012XX_, 7) 0);
wchar_t buf[10];
assert (sl.copy(buf, sizeof (buf) I sizeof (wchar_t» 6
&& wcscmp(buf, L012XX_) == 0);
assert (sl.copy(buf, 3, 1) == 3
&& wmememp(buf, L12X, 3) == 0);
II test finds
sl = Ls4s4;
assert(sl.find(s4) == 0 && sl.find(s4, 1) == 2
&& sl.find(s4, 3) == NPOS);
assert(sl.find(Ls4) == 0 && sl.find(Ls4, 3) == NPOS
&& sl.find(Ls4XX, 1, 2) == 2);
assert(sl.find(_L('s'» == 0 && sl.find(_L('s'), 1) 2
&& sl.find(_L('x') == NPOS);
assert(sl.rfind(s4) == 2 && sl.rfind(s4, 1) == 0
&& sl.rfind(s5, 3) == NPOS);
assert (sl.rfind(Ls4) == 2 && sl.rfind(Ls4, 3) == 2
&& sl.rfind(Ls4XX, 1, 3) == NPOS);
assert(sl.rfind(_L('s'» == 2 && sl.rfind(_L('s'), 2) 2
&& sl.rfind(_L('x'» == NPOS);
assert (sl.find_first_of(s4) == 0
&& sl.find_first_of(s4, 1) 1
&& sl.find_first_of(s4, 4) == NPOS);
assert (sl.find_first_of(Ls4) == 0
&& sl.find_first_of(Ls4, 3) == 3
&& sl.find_first_of(Labs, 1, 2) == NPOS);
assert(sl.find_first_of(_L('s'» == 0
&& sl.find_first_of(_L('s'), 1) 2
&& sl.find_first_of(_L('x'» == NPOS);
<wstring> 419
Background
A common practice among programmers has long been to use flag and
mask words. Both of these are objects typically of some integer type. But they
do not store a range of integer values. Rather:
flag word • Each bit field of a flag word represents an independent Boolean value.
mask word • Groups of one or more bits in a mask are set to select, by a bitwise AND
operation, corresponding mask fields in integer values of the same size.
I group these two uses together because they often overlap. Consider the
bitmask type ios: : fmtflags. It has both bit fields, such as skipws, and
mask fields, such as floatfield. (See Chapter 6: <ios>.)
The idiom in C has long been to define macros that specify each bit and
mask field, as in:
#define SKIPWS Ox80
#define FLOATFIELD Ox70
You can then define flag and mask words of some integer type and perform
bitwise operations between such objects and these macros.
The limitations of this approach are obvious:
• The translator can perform no type checking, to ensure that only the
relevant macros mix with flag or mask words.
• You have to contrive non-overlapping bit and mask fields by hand.
Adding or changing a field is both tiresome and error prone.
• When you exhaust the bits in the largest available integer type, you must
indulge in significant rewrites to add more bit or mask fields.
bitmosk Enumerations help some with the first limitation. The draft C++ Stand-
types ard improves matters further by tightening the type checking on enumera-
tions. (See page 121.) Indeed, the bitmask types are used throughout the
Standard C++ library to replace old-fashioned flag words. But bitmask
types still suffer from the remaining limitations listed above.
<bits> The Committee adopted the header <bits> as a way to overcome all
three limitations. It defines the template class bits<N>, and a handful of
related functions. The parameter N, of type size_t, specifies the number of
elements in an object of this class. You can perform bitwise operations on
422 Chapter 17
this class by much the same rules as for the basic integer types, and with
the same notation, regardless of the number of bits in a particular instan-
tiation. You can also designate an element by its position (counting from
zero), rather than writing explicit shifting and masking code.
Agood implementation of this template class is presumably economical
in its use of storage. In particular, the idea is that:
• for small enough values of N, a single integer stores all the bits and the
generated code is as efficient as traditional C code
• for larger values of N, multiple integers store all the bits and the gener-
ated code loops over tllese objects as needed
You still need to contrive names for the bits in a bits<N> object, but a
conventional enumeration can now define positions for the distinct bit
fields in the object. And, unlike older implementations of flag words, you
can more easily loop over subranges of bits, just by varying a position value.
template I've already shown one header that defines templates, for the iostreams
limitations manipulators that take an argument. (See Chapter 10: <iomanip>.) I've also
discussed the current limitations of template technology, on page 120 and
on page 252. The template class bits<N> is far more ambitious than any of
these template classes. It is also an invention of the Committee. Some
implementations, particularly those that endeavor to be portable, are still
hampered by available technolog~ And some would-be users are still
hampered by lack of implementations, and sheer lack of time to build
experience. It is thus too soon to tell whether programmers will widely
abandon conventional flag and mask words in favor of this newer and more
disciplined technolog~
Footnotes:
Footnotes 113) An implementation is free to store the bit sequence more efficiently.
Future Directions
Standard A very ambitious recent addition to the Standard C++ Library is an
Template extensive Standard Template Library, or STL (S&L94). It provides a uniform
Library and powerful set of templates comparable in size to all the code shown in
this book. It offers considerable promise for future implementations of the
draft C++ Standard. At the moment, however, it strains the current tem-
plate technology. Moreover, the defintion of STL makes extensive use of
default template parameters and member templates, two extensions only
recently added to the draft C++ Standard.
STL may eventually replace the header <bits>, and most of the headers
that follow. Still, I've chosen to cover fully the header <bits> because:
• It is still part of the Informal Review Draft, which forms the basis for this
implementation of the Standard C++ Library.
• It describes functionality that should be retained, in one form or another,
in future versions of the draft C++ Standard.
• It has useful tutorial benefit, for those who want to see how a template
class of this nature can be constructed and used.
• It has useful practical benefit, for those who want to use such a template
class whether or not it remains a part of the draft C++ Standard.
I note for each of the headers that follow if it is likely to be in the same boat.
But I also continue to describe each one, for much the same reasons.
428 Chapter 17
Using <bits>
You include the header <bits> to make use of the template class
bits<N>. Here,N is an integer constant expression that specifies the number
of bits in an instantiation of the template class. Objects of such a template
class let you represent and manipulate a fixed-length sequence of bits.
Earlier in this chapter, I dubbed such objects flag and mask words. The
advantage here is that you can manipulate all objects of template class
bits<N> the same way irrespective of the number of words required for
their representations.
construction To construct a bits<N> object xO, say of size 20 bits, with all bits initially
zero, you can simply write:
bits<20> xO;
length Eachobjectx reports the length of its bit sequence with
the call x •length ( ). That wa)', you don't have to reconstruct the value
N, or store it separately, from when you first instantiate the class until you
later need it again.
One style for determining the size of a particular instantiation is in
conjunction with the enumeration constants that designate its elements, as
in:
enum color (red, green, blue, •.••• , N);
typedef bits<N> colorset;
You append N to the list of enumeration constants you define to access
different elements (bits) of colorset objects. That wa)', the translator
determines the number of bits for you.
You can also construct a bits<N> object and initialize its elements from
an unsigned integer value, as in:
bits<32> xl(Ox55555555); II set every other bit
In this case, the least-significant bit in the integer argument determines
element zero. More generally, the initializer stores the value one in element
j if, for the argument val, the expression val &: (1 « j) is nonzero.
Finally, you can construct a bits<N> object and initialize its elements from
corresponding elements of a character sequence controlled by a string
object. (See Chapter 15: <string>.) The character value' 0' initializes the
element to zero, and the character value' l ' initializes the element to one.
No other character values are permitted. As usual with string arguments,
you can specify one or two additional arguments to select a substring. In
the examples that follow, the comment after each constructor indicates the
constructor with equivalent effect:
string alt("10101");
bits<10> x2(alt); II bits<10> x2(Ox15)
bits<10> x3(alt, 1); II bits<10> x3(OxOa)
bits<10> x4(alt, 1, 3); 1/ bits<10> x4(Ox02)
An unsigned integer or string object that specifies more than N bits causes
no problems. The excess bits are simply ignored.
<bits> 429
bitwise Given two objects xO and xl of class bits<N> (for the same value ofN,
operations of course), you can perform the usual bitwise operations:
xO = xl
xO &= xl xO & xl
xO 1= xl xO I xl
xO A= xl xO A xl
xO «= m xO « m
xO »= m xO » m
xO == xl xO 1= xl
Here, m is an expression with some integer type. The semantics of all these
operations involving xO and xl are a natural extension of those for un-
signed integer values.
You can deal with all bits at once, by writing:
xO.set() II set all bits
xO.reset() II clear all bits
xO.toggle() II toggle (invert) all bits
-xO II toggle all bits
xO.count() II count all set bits
xO.any() II test if any bit set
xO.none() II test if no bit set
Or, given a valid bit position n, you can affect just that bit, by writing:
xO.set(n) II set bit n
xO.set(n, b) II set bit n to (b != 0)
xO.reset(n) II clear bit n
xO.toggle(n) II toggle bit n
xO.test(n) II test bit n
conversions The constructors, shown above, define mappings from unsigned inte-
gers or string objects to bits<N> objects. You can perform the inverse
mappings as well:
xO.to_ushort() II return unsigned short equivalent
xo. to_ulong ( ) I I return unsigned long equivalent
xO.to_string() II return string equivalent
The first two member functions can report an overflow error (by throwing
an exception) if the corresponding value is too large to represent as the
return type. Absent any overflow, such a return value should serve as a
constructor argument that generates a bits<N> object equal to xo.
Finally, you can insert and extract objects of class bits<N>. For example:
extractor cin » xO
effectively extracts a string object from the standard input stream and
assigns it to some temporal)'- (See Chapter 15: <string>.) Unlike the usual
string extractor, however, the function:
• does not use the width field stored in cin, nor does it set the field to zero
• extracts at most Ncharacters, after skipping any white space
• otherwise extracts up to, but not including, the first character that is not
o or 1
The function then converts the controlled character sequence, by the same
rules as for constructing a bits<N> object from a string argument, and
assigns it to xO.
430 Chapter 17
Last of all, you can insert the character sequence into, say, the standard
output stream, by writing:
inserter cout « xO
As you might expect, this is equivalent to inserting xO • to_string ( ) .
I won't recite all the reasons for choosing these particular derived parame-
ter values. All I can say is, they were the result of several rewrites to make
the code at once compact, robust, and readable.
_BITS_BYTE The definition of _Nb involves the macro _BITS_BYTE. It is defined in
<yxvals •h> with the obvious meaning - it counts the number of bits in a
single byte. Yes, this is exactly the same value as the macro CHAR_BiTS,
defined in <limits.h>. The latter macro cannot, however, be defined in a
program that includes only the header <bits>. So I use a secret alias
instead.
<bits> 431
Figure 17.1: II bits standard header
#ifndef _BITS_
bits
#define BITS
Part 1 of 4 #include <string>
II template class bits
string _Bitsxstr(istream&, size_t};
template<size_t _N> class bits {
typedef unsigned long _T;
public:
bits(}
{_Tidy(}; }
bits(unsigned long _X}
{_Tidy ( };
for (size_t _P = 0; _X 1= 0 && _P < _N; _X »= 1, ++_P)
if (_X & 1)
set(_P}; }
bits(const string& _S, size_t _P 0, size_t _L NPOS}
{if (_S.length() < _P}
_Xran(};
if (_S.length() - _P < _L}
_L = _S.length(} - _PI
if (_N < _L)
_L= _N;
_Tidy(}, _P += _L;
for (size_t _I = 0; _I < _L; ++_I)
if (_S[--_P]== '1')
set(_I};
else if (_S [_P] ! = ' 0' )
_Xinv(}; }
bits<_N>& operator&=(const bits<_N>& _R}
{for (int _I = _Nw; 0 <= _I; --_I)
_A[_I] &= _R._W(_I};
return (*this); }
bits<_N>& operatorl=(const bits<_N>& _R}
{for (int _I = _Nw; 0 <= _I; --_I)
_A[_I]1= _R._W(_I};
return (*this); }
bits<_N>& operatorA=(const bits<_N>& _R}
{for (int _I = _Nw; 0 <= _I; --_I)
_A[_I] A= _R._W(_I};
return (*this); }
bits<_N>& operator«=(size_t _P}
{if (_P < O)
return (*this »= -_P);
const int _D = _P I _Nb;
if <_D 1= 0)
for (int _I = _Nw; 0 <= _I; --_I)
_A[_I] = _D <= I ? _A[_I - _D] 0;
if «_P %= _Nb) != O}
{for (int _I = _Nw; 0 < _I; --_I)
_A[_I] = (_A[_I] « _P)
I (_A[_I - 1] » (_Nb - _P)};
_A[O] «= _P, _Trim(); }
return (*this); }
bits<_N>& operator»=(size_t _P}
432 Chapter 17
Continuing {if (_P < 0)
return (*this «= -_P);
bits
const int _D = _P I _Nb;
Part 2 of 4 if (_D != 0)
for (int _I = 0; _I <= _Nw; ++_1)
_A[_I] = _D <= _Nw - _I ? _A[_I + _D] 0;
if «_P %= _Nb) != 0)
{for (int _I = 0; _I < _Nw; ++_1)
_A[_I] = (_A[_I] » _P)
1 (_A[_I + 1] « (_Nb - _P»;
_A [_Nw] »= _P, }
return (*this); }
bits<_N>& set()
{_Tidy(-(_T)O);
return (*this);
bits<_N>& set(size_t _P, _Boo1 _X 1)
{if (_N <= _P)
_Xran();
if (_X)
_Al_P I _Nb] 1= (_T)l « _P % _Nb;
else
_Al_P I _Nb] &= -«_T)l « _P % _Nb);
return (*this); }
bits<_N>& reset()
{_Tidy ( );
return (*this); }
bits<_N>& reset(size_t _P)
{return (set(_P, 0»;
bits<_N> operator-C) const
{return (bits<_N>(*this).togg1e(»;
bits<_N>& togg1e()
{for (int _I = _Nw; a <= _I; --_I)
_A[_I]= -_A[_I];
_Trim ( );
return (*this); }
bits<_N>& togg1e(size_t _P)
{if (_N <= _P)
_Xran() ;
_A[_P I _Nb] A= (_T)l « _P % _Nb;
return (*this); }
unsigned short to_ushort() const
{unsigned long _V = to_ulong();
if (-(unsigned short)O < _V)
_Xof10();
return (_V); }
unsigned long to_ulong() const
{enum {_Assert = 1 I
(sizeof (unsigned long) % sizeof (_T)== a)};
int _I = _Nw;
for (; sizeof (unsigned long) I sizeof (_T) <= _I; --_I)
if (_A[_I] ! = 0)
_Xof10();
for (unsigned long _V = _A[_I]; a <= --_I; )
_V = _v « _Nb 1 _A[_I];
return (_V); }
<bits> 433
The template class bits<N> also defines five secret protected member
functions:
_Tidy • _Tidy, which initializes the array elements all to the same value (usually
zero)
_Trim - _Trim, which sets to zero any unused bits in the last element of the arra~
both for good hygiene and to simplify several bitwise operations
_Xinv II _Xinv, which reports an invalid-argument error
_Xoflo • _Xoflo, which reports an overflow error
_Xran - _Xran, which reports a range error
For a discussion of the exceptions associated with invalid-argument, over-
flow, and range errors, see Chapter 3: <exception>. As you can see, all of
these functions are used throughout the member function definitions.
template None of the member function definitions is particularly complex. Nev-
limitations ertheless, several of the larger ones are poor candidates for making inline,
as they are declared here. As I have discussed earlier (page 253), some
translators refuse to make inline any definition that includes a looping
statement. (Some simply refuse to translate the code at all, even though
they are supposed to.) Others may generate inline code, but slavishly loop
even over a single word.
Obviousl)', this code works best in conjunction with a smart template
optimizer. Such creatures are currently in short supply, but that should not
always be the case. The draft c++ Standard is banking ever more heavily
on template technolog)T, and not just within the Standard C++ library: If
such pressures continue to drive the commercial compiler indust~ then
the kind of code I show here will eventually come into its own.
Meanwhile, I can report that this code does translate successfully under
at least some current implementations. To make more than nontrivial use
of it, however, you should check carefully to see the kind of code it
generates. You may well want to modify it.
<bits> 435
Figure 17.2: II bitsxstr -- _Bitsxstr(istream&, size_t)
#include <bitstring>
bitsxstr.c
string _Bitsxstr(istream& is, size_t n)
{ II extract into a string of text bits
_Bool changed = 0;
string str(n, reserve);
if (n == NPOS)
--n;
_TRY_IO_BEGIN
if (is. ipfx ( ) )
{ II extract arbitrary characters
int chI
while (0 < n && (ch = is.rdbuf()->sbumpc(» != EOF)
if (ch != '0' && ch != '1')
{ II put back non-bit and quit
is.rdbuf()->sputbackc(ch);
break;
else
str.append«char)ch), changed 1, --n;
}
if (!changed)
is.setstate(ios::failbit);
is.isfx();
_CATCH_IO_(is);
return (str);
o
bitsxstr.c The header <bits> needs only one additional source file. Figure 17.2
shows the file bitsxstr.c, which defines the function _Bitsxstr. It is
quite similar to the corresponding extractor for class string. (See Chapter
15: <string>.) I described the differences earlier in this chapter, on page
429. The function extracts a character sequence of maximum length n,
consisting only of the characters 0 and 1.
For a description of the exception-handling macros, see page 128.
Testing <bits>
tbits.c Figure 17.3 shows the file tbits.c. It tests the basic properties of the
template class bits<N> defined in <bits>. It does so by instantiating the
class bi t s< 5> and testing the various operations, at least for their minimum
functionality: It then instantiates the class <153>, as an example of a larger
parameter value, and performs a few more cursory tests.
If all goes well, the program prints:
SUCCESS testing <bits>
and takes a normal exit.
436 Chapter 17
Figure 17.3: II test <bits>
#include <assert.h>
tbitsoc
#include <string.h>
Part 1 of 2 #include <bits>
#include <iostream>
#include <strstream>
int main()
{ II test basic workings of bits definitions
bits<S> xSO, xSl(Oxf), xS2(string(lIxxl010lab ll ), 2, lS);
bits<S> xS3(xS2);
assert(xSO.to_ushort() == OxOO);
assert(xSl.to_ulong() OxOf);
assert (xS2.to_ulong() == OxlS);
assert(xS3.to_ulong() == OxlS);
II test arithmetic
xSO 1= xSl, assert(xSO.to_ulong() OxOf);
xSO A= xS2, assert(xSO.to_ulong() Oxla);
xSO &= xSl, assert(xSO.to_ulong() OxOa);
xSO «= 2, assert (xSO.to_ulong() == OxOS);
xSO »= 3, assert (xSO.to_ulong() == OxOl);
xSO.set(2), assert(xSO.to_ulong() == OxOS);
xSO.set(O, 0), assert(xSO.to_ulong() == Ox04);
xSO.set(), assert(xSO.to_ulong() == Oxlf);
xSO.reset(3), assert (xSO.to_ulong() == Ox17);
xSO.reset(), assert (xSO.to_ulong() == OxOO);
xSO.toggle(2), assert(xSO.to_ulong() == Ox04);
xSO.toggle(), assert (xSO.to_ulong() == Oxlb);
assert (xSO.to_string() == string(lIllOllll»;
assert (xSO.count() == 4 && xS2.count() == 3);
assert (xSO.length() == S && xSl.length() == S);
assert(xSO == xSO && xSO != xSl);
assert(xSO.test(l) && !xSO.test(2»;
assert(xSO.any() && !xSO.none(»;
xSO.reset(), assert(!xSO.any() && xSO.none(»;
II test friend arithmetic functions
xSO = xSl;
assert«xSO « 2).to_ulong() == Oxlc
&& (xSO » 2).to_ulong() == Ox03);
assert«xSO & xS2) == bits<S>(OxOS)
&& (xSO & OxOS) == bits<S>(OxOS)
&& (OxOS & xSO) == bits<S>(OxOS»;
assert«xSO I xS2) == bits<S>(Oxlf)
&& (xSO I OxlS) == (OxlS I xSO»;
assert«xSO A xS2) == bits<S>(Oxla)
&& (xSO A OxlS) bits<S>(string(lllOlO"»
&& (OxlS A xSO) == (xSO A OxlS»;
II test 1/0
istrstream ins(1I1 0101 11000
11
);
ostrstream outs;
ins » xSO, assert(xSO.to_ulong() axOl);
outs « xSO « ' ';
ins » xSO, assert (xSO.to_ulong() OxOS);
outs « xSO « ' ';
ins » xSO, assert (xSO.to_ulong() OxlS);
<bits> 437
Exercises
Exercise 17. 1 Describe how you might use template class bits<N> to perform extended-
precision arithmetic.
Exercise 17.2 What is the meaning of the instantiation bits<O>? Is it permitted? Is it
useful? Does the code in file bits survive such an instantiation?
Exercise 17.3 Does the code in file bits survive the instantiation bits<NPOS>?
Exercise 17.4 Implement the class bits32 tllat has the same functionality as bits<32>.
Exercise 17.5 Change the definition of _Nw in file bi ts to be one greater, and hence COUll t
the number of words in the array of unsigned integers. Alter the remaining
code to work correctly with this revised definition.
Exercise 17.6 [Harder] Rewrite the definition of template class bits<N> for your imple-
mentation so that it generates the most optimal code. Can you get the
looping code to disappear for small enough values of N?
Exercise 17.7 [Very hard] Write a portable definition of template class bits<N> that
assuredly contains no loops over array elements when N <= 32.
438 Chapter 17
Chapter 18: <bitstring>
Background
The header <bi tstring> defines the class bi tstring, and a few sup-
porting functions. Like an object of class string, described in an earlier
chapter, a bitstring object controls a sequence that can vary in length as
the program executes. (See Chapter 15: <string>.) Where the former
controls a character sequence, however, the latter controls a bit sequence.
You could achieve the same effect by using only two values in a character
sequence and recycling class string. Presumabl)', howeve~ class bit-
string offers greater storage economy: A reasonable implementation
should pack those bits into integer objects of a more convenient size.
Class bitstring also borrows heavily from another predecessor in the
Standard C++ libra~ The template class bits<N>, described in the pre-
vious chapter, deals with fixed-length sequences of bits. Both classes have
reason to overload the bitwise operators defined on the integer types. And
both classes have reason to define simple logical operations on individual
bits.
What you will find here, then, is a heady mixture of operations. You can
insert, delete, and replace subsequences within a bit sequence. You can also
AND, OR, and EXCLUSIVE OR bit sequences. Class bitstring is thus
more suitable for manipulating, sa)', sequences of pixels in an image than
either class string or the template class bits<N'>.
Future Directions
bit_string The name of the class has been changed to bit_string.
Standard As I first mentioned in the previous chapter, the Standard Template
Template Library (STL) may replace the header <bitstring>. Class bi tstring is not
Library a template class, but neither does it enjoy the central importance of class
string, which is unlikely to be displaced by such a change. A combination
of more general templates from STLmay prove to be an adequate substitute
for an explicit class bitstring. Nevertheless, I provide a full discussion of
the header <bitstring>, for the reasons I outlined on page 427.
If class bitstring survives, its behavior will probably be reconciled in
a number of small ways with class string (and class wstring).
Using <bitstring>
You include the header <bitstring> to make use of the class bit-
string. Objects of this class let you manipulate in-memory bit sequences
that vary dynamically in length. The bi tstring object allocates and frees
storage as needed to accommodate changes in length of the controlled
sequence.
construction To construct a bitstring object xO with an initially empty bit sequence,
you can simply write:
bitstring xO;
length Each object x reports the length of its bit sequence with the call x.
length ( ). (Class bitstring does not, however, support a reserve size as
does class string.)
To construct a bi tstring and define its initial bit sequence, you have
several choices. In each case below, the comment shows the resulting bit
sequence, beginning with bit position zero. You can, for example, construct
a bitstring from an unsigned int initial value by writing such things as:
bitstring xl(Ox12) II 10010
bitstring x2(Ox12, 1) II 10010
bitstring x3(Ox12, 8) II 10010000
As I noted earlier in this chapter, on page 439, the significance of bits is
reversed from that for the template class bits<N>. If you omit the second
argument, the sequence exactly captures all significance. If the second
argument calls for more bits, the constructor effectively scales the initial
value by a power of 2. Bit zero is set for any nonzero initial value.
<bitstring> 449
You can also construct a bitstring from a string object, by writing
such things as:
string str{"01001");
bitstring x4{str) II 01001
bitstring x5{str, 1) II 1001
bitstring x6{str, 1, 3) II 100
I described the rules for selecting a substring from a string object much
earlier, on page 358.
substrings Finall)T, you can define the initial bit sequence when you construct a
bitstring object by selecting a substring from another bitstring object.
The arguments behave the same as those for selecting a substring from a
string argument, above. Given the definition of x4 above, you can write:
bitstring x7{x4); II 01001
bitstring x8{x4, 1); II 1001
bitstring x9{x4, 1, 3); II 100
notation For brevit)r, I define here a terse and uniform style for writing various
argument combinations. The resultant argument is:
• an entire bitstring object, with the argument x
• a substring beginning at position pos, witll the arguments x, pos
• a substring of n bits beginning at position pos, with the arguments x,
pos, n
With this notation, I can more quickly summarize all the ways you can
perform various operations involving bitstring objects. Here, for exam-
ple, is a summary of the last three constructors I showed above:
bitstring x7 (x) ;
bitstring x8{x, pos);
bitstring x9{x, pos, n);
string The presentation that follows shows the parallels first with class string,
operations then with template class bi ts <N>. A number of member functions alter the
bit sequence controlled by a bitstring object xO.
To assign a new bit sequence, in place of any existing one:
assign xO.assign(x) xO = x
xO.assign(x, pos)
xO.assign{x, pos, n)
To append to an existing bit sequence:
append xO.append(x) xO += x
xO.append{x, pos)
xO.append(x, pos, n)
To insert before position px in an existing bit sequence:
insert xO.insert(px, x)
xO.insert(px, x, pos)
xO.insert(px, x, pos, n)
To replace at most m bits beginning with position px in an existing bit
sequence:
replace xO.replace(px, m, x)
xO.replace(px, m, x, pos)
xO.replace(px, m, x, pos, n)
450 Chapter 18
You can remove a substring from a bit sequence in various ways:
remove xO. remove () I I remove all bits
xO • remove (px) I I remove all from px to end
xO. remove (px, n) I I remove at most n beginning at px
You can establish a new length len for a bit sequence. If the new length
is greater than the existing length, the bit sequence is padded as stated in
the comment below:
resize xO.resize(len) II pad to len with zero bits
xO.resize(len, b) II pad to len by repeating (b 1= 0)
As a final alteration, you can remove any trailing zero bits:
trim xO.trim() II remove trailing zero bits
If the sequence has no bits set, it is removed completely: Otherwise, the last
bit in the remaining sequence is set.
Some functions each construct a string object to return as the value of
the function, leaving the object xO unaltered. You can, for example, obtain
a substring as a separate object:
substr xO.substr() II copy all
xO.substr(pos) II copy remainder beginning at pos
xO.substr(pos, n) II copy at most n beginning at pos
You can also construct a bitstring object that appends the character
sequences defined by two operands:
operator+ x + xO
Comparing two bitstring objects involves a bit-by-bit comparison of
corresponding values in the two bit sequences. If the two bit sequences are
not of equal length, they do not compare equal. To compare two bit
sequences for equality (==) or inequality ( ! =), you can write:
operator== x == xO
x != xO
A number of member functions look for a given bit setting within a bit
sequence. To find the first (lowest position) occurrence that matches b ! =
o within a bit sequence:
find xO.find(b) II find first in xO
xO.find(b, px) II find first on or after px
xO.find(b, px, n) II find first in xO.substr(px, n)
All such calls return the position of a successful match, or NPOS to report
failure.
Similarl)', to find the last (highest position) occurrence that matches b ! =
o within a bit sequence:
rfind xO.rfind(b) II find last in xO
xO.rfind(b, px) II find last on or after px
xO.rfind(b, px, n) II find last in xO.substr(px, n)
Note that the arguments for the member functions find and rfind differ
significantly between class string and class bitstring. Study both care-
fully lest you misuse either.
<bitstring> 451
bitwise Now for the operations that parallel those for the template class
operations bits<N>. Given two objects xO and xl of class bitstring, you can perform
the usual bitwise operations:
xO &= xl xO & xl
xO 1= xl xO 1 xl
xO A= xl xO A xl
xO «= m xO « m
xO »= m xO » m
Here, m is an expression with some integer type. The semantics of all these
operations involving xO and xl are a natural extension of those for un-
signed integer values with an added proviso. If the two operands are of
differing length, the shorter is effectively padded to the same length by
appending zero bits. The resulting bit sequence has this greater length,
regardless of which operand was initially shorter.
You can deal with all bits at once, by writing:
xO.set() II set all bits
xO.reset() II clear all bits
xO.toggle() II toggle (invert) all bits
-xO II toggle all bits
xO.count() II count all set bits
xO.any() II test if any bit set
xO.none() II test if no bit set
Or, given a valid bit position n, you can affect just that bit, by writing:
xO.set(n) II set bit n
xO.set(n, b) II set bit n to (b 1= 0)
xO.reset(n) II clear bit n
xO.toggle(n) II toggle bit n
xO.test(n) II test bit n
conversions The constructors, shown above, define mappings from unsigned inte-
gers or string objects to bitstring objects. To go the other wa)T, write:
xO.to_string() II return string equivalent
The return value can serve as a single constructor argument that generates
a bi tstring object equal to xo. To map a bit sequence to a numerical value,
use the to_ushort or to_ulong member functions in class string. (See
Chapter 15: <string>.)
Finally, you can insert and extract objects of class bitstring, as in:
extractor cin » xO
effectively extracts a bi tstring object from the standard input stream and
assigns it to some temporary. The function follows the same special rules
for extracting the string as does the extractor for the template class bit s<N>,
as described on page 429. The function then converts the controlled char-
acter sequence, by the same rules as for constructing a bi tstring object
from a string argument, and assigns it to xo.
Last of all, you can insert the character sequence into, say, the standard
output stream, by writing:
inserter cout « xO
As you might expect, this is equivalent to inserting xo. to_string ( ).
452 Chapter 18
Figure 18. 1: II bitstring standard header
#ifndef _BiTSTRiNG_
bitstrin #define _BiTSTRiNG_
Part 1 of 3 #inc1ude <string>
II class bitstring
string _Bitsxstr(istream&, size_t);
class bitstring {
public:
typedef unsigned long _T;
enum {_Nb = _BiTS_BYTE * sizeof (_T)};
enum _Source {_Zeros = 0, _Ones = -1};
bitstring()
{_Tidy ( ); }
bitstring(unsigned long, size_t);
bitstring(const bitstring& _X, size_t _P 0,
size_t _N = NPOS)
{_Tidy(), assign(_X, _P, _N); }
bitstring(const string&, size_t = 0, size_t = NPOS);
bitstring(_Source _S)
: _Ptr(O), _Src(_S), _Len(O), _Res(O) {}
-bitstring ( )
{_Tidy(l); }
bitstring& operator=(const bitstring& _R)
{return (assign(_R»; }
bitstring& operator+=(const bitstring& _R)
{return (append(_R»; }
bitstring& operator&=(const bitstring&);
bitstring& operatorl=(const bitstring&);
bitstring& operatorA=(const bitstring&);
bitstring& operator«=(size_t);
bitstring& operator»=(size_t);
bitstring& append(const bitstring&, size_t 0,
size_t = NPOS);
bitstring& assign(const bitstring&, size_t = 0,
size_t= NPOS);
bitstring& insert(size_t, const bitstring&, size_t 0,
size_t = NPOS);
bitstring& remove(size_t = 0, size_t = NPOS);
bitstring& rep1ace(size_t, size_t, const bitstring&,
size_t = 0, size_t = NPOS);
bitstring& set();
bitstring& set(size_t, _Bool = 1);
bitstring& reset();
bitstring& reset(size_t _P)
{return (set(_P, 0»; }
bitstring& toggle();
bitstring& togg1e(size_t);
string to_string() const;
size_t count() const;
size_t length() const
{return (_Len); }
size_t resize(size_t, _Boo1 0);
size_t trim()
{resize(rfind(l) + 1);
return (_Len); }
<bitstring> 453
Continuing size_t find {_Bool, size_t = 0, size_t = NPOS) const;
bitstrin size_t rfind{_Bool, size_t = 0, size_t =
NPOS) const;
bitstring substr{size_t _P, size_t _N = NPOS) const
Part 2 of 3 {return (bitstring{*this, _P, _N»; }
_Bool operator=={const bitstring&) const;
_Bool operatorl={const bitstring& _R) const
{return (I{*this == _R»; }
_Bool test{size_t) const;
_Bool any() const;
_Bool none{) const
{return (!any{»; }
bitstring operator«{size_t _R) const
{return (bitstring(*this) «= _R);
bitstring operator»{size_t _R) const
{return (bitstring(*this) »= _R);
bitstring operator-C) const
{return (bitstring(*this).toggle(»;
_T _W{int _I) const
{return (_Ptr[_I]); }
_T _X(int _I, int _P) const
{int L = Len == 0 ? -1 : (_Len - 1) I _Nb;
return (_L < _I? _Src: _P == 0 ? _Ptr[_I]
_L == _I ? _Ptr[_I] « _P
_Ptr[_I] « _P 1 _Ptr[_I + 1] » _Nb - _P);
private:
void _Copylr{const bitstring&, size_t, size_t, size_t);
void _Copyrl{const bitstring&, size_t, size_t, size_t);
_Bool _Grow{size_t, _Bool = 0);
void _Setl{size_t _L)
{_Len = _L, _L %= _Nb;
if (_L != 0)
_Ptr[(_Len - 1) I _Nb] &= -(-{_T)O » _L); }
void _Tidy(_Bool = 0);
void _Xinv{) const
{invalidargument{"invalid bitstring char").raise();
void _Xlen{) const
{lengtherror("bitstring too long").raise{); }
void _Xran{) const
{outofrange("invalid bitstring position").raise{);
_T *_ptr, _Src;
size_t _Len, _Res;
};
II operators
inline bitstring operator+{const bitstring& _L,
const bitstring& _R)
{return (bitstring{_L) += _R); }
inline bitstring operator&{const bitstring& _L,
const bitstring& _R)
{return (bitstring{_L) &= _R); }
inline bitstring operator 1 (const bitstring& _L,
const bitstring& _R)
{return (bitstring{_L) 1= _R); }
inline bitstring operatorA{const bitstring& _L,
const bitstring& _R)
{return (bitstring{_L) A= _R); }
454 Chapter 18
Continuing in1ine istream& operator»(istream& _I, bitstring& _R)
{_R = _Bitsxstr(_I, NPOS - 1);
bitstrin
return (_I); }
Part 3 of 3 in1ine ostream& operator«(ostream& _0, const bitstring& _R)
{return (_0 « _R.to_string(»; }
#endif o
Implementing <bitstring>
bitstrin Figure 18.1 shows the file bitstrin, which implements the header
<bitstring>. Its principal business is to define the class bitstring.
_T Class bitstring includes two type definitions and several enumeration
constants. The first type definition is _T. An object of class bitstring stores
an array of one or more unsigned integer member objects. _T is the type of
the array elements. I have chosen unsigned long for this type, but you might
prefer unsigned short or unsigned char. Some implementations may manipu-
late the smaller element types more efficientl~ Or you may declare large
arrays of bitstring and want to keep object sizes smaller.
_Source An added constructor has as its single parameter type the enumeration
_Zeros _Source. It constructs a special form ofbitstring object jiggered to supply
_Ones an arbitrary-length bit sequence. An argument value of _Zeros specifies
that all bits in the sequence be zeros. An argument value of _Ones specifies
that all bits in the sequence be ones.
_Nb The last constant is _Nb, the number of bits in an array element Its
B:ITS_BYTE definition involves the macro _B:ITS_BYTE, defined in <yxvals .h> as the
number of bits in a single byte. (See page 430.)
o
456 Chapter 18
Figure 18.4: II bitscstr -- bitstring::bitstring(const string&, size_t,
bitscstr.c II size_t)
#include <bitstring>
return (*this);
D
<bitstring> 457
Figure 18.6: II bitsor -- bitstring::operatorl=(const bitstring&)
#include <bitstring>
bitsor.c
bitstring& bitstring::operatorl=(const bitstring& str)
{ II OR a bitstring into a bitstring
size_t m, n;
if (str.length() < _Len)
m str.length(), n = _Len;
else
m _Len, n = str.length();
if (_Grow(n»
{ II OR into non-empty string
int mw = ==m 0 ? -1 : (m - 1) I _Nb;
int nw = (n - 1) I _Nb;
for (int i = 0; i <= mw; ++i)
_Ptr[i] 1= str._W(i);
if (_Len < n)
while (++mw <= nw)
_Ptr[mw] = str._W(mw);
_Len = n;
return (*this);
o
return (*this);
o
458 Chapter 18
Figure 18.8: II bitslsh -- bitstring::operator«=(size_t)
#include <bitstring>
bitslsh.c
bitstring& bitstring::operator«={size_t pos)
{ II shift left a bitstring
if Cpos < 0)
*this »= -pos;
else if (O < _Len && 0 < pos)
_Copylr(*this, 0, pos, _Len);
The member function _Grow calls bytes to determine the size of requests
for more storage. It also uses the parameter trim as an indication that
shrinking storage may be a good idea. The function is otherwise reluctant
to do so.
realloc Note that _Grow allocates, or reallocates, storage by calling realloc,
declared in <stdlib.h>. That function can sometimes adjust the size of
allocated storage more efficiently than the equivalent combination of new
and delete expressions. For a description of the function _Nomemory, see
page 48.
bitsculo.c Figure 18.3 shows the file bitsculo.c, which defines the constructor
bitstring (unsigned long ulo, size_t n). It converts ulo to an
equivalent bit sequence whose length is at least n. Note that bits are packed
into array elements from left (most significant) to right. Moreover, bit zero
in the sequence is the most significant bit numerically: This convention is
backwards from that for the template class bits<N>, described in the
previous chapter.
460 Chapter 18
Figure 18.11: II bitscprl -- bitstring::_Copyrl(const bitstring&, size_t,
bitscprl.c II size_t, size_t)
#include <bitstring>
bitscstr.c Figure 18.4 shows the file bitscstr.c, which defines the constructor
bitstring{const string& str, size_t pos, size_t ns).Itconverts
the substring str. substr (pos, ns) to an equivalent bit sequence of the
same length.
bitsand.c Three source files define similar member functions for the assigning
bitsor.c bitwise operators between two bitstring objects. Figure 18.5 shows the
bitsxor.c file bitsand.c, which. defines operator&=. Figure 18.6 shows the file
bitsor. c, which defines operator I=. And Figure 18.7 shows the file
bitsxor.c, which defines operator"=. The major difference between
these three member functions is how they handle the implicit padding of
a shorter operand with zero bits.
bitslsh.c Another pair of source files defines similar member functions for the
bitsrsh.c assigning shift operators. Figure 18.8 shows the file bitslsh.c, which
defines operator«=. And Figure 18.9 shows the file bitsrsh.c, which
defines operator»=. They depend on the internal member functions
_Copylr and _Copyrl, mentioned earlier. Note the use of the constant
object zeros as a source of an arbitrary-length sequence of zero bits.
<bitstring> 461
Figure 18. 12: II bitsapp -- bitstring::append(const bitstring&, size_t, size_t)
#include <bitstring>
bitsapp.c
bitstring& bitstring::append(const bitstring& str, size_t pos,
size_t ns)
{ II append a bitstring to a bitstring
if (str.length() < pos)
_JCran() ;
size_t n = str.length() - pos;
if (n < ns)
ns = n;
if (NPOS - _Len <= ns)
_Xlen() ;
if (0 < ns)
{ II make new non-empty string
n = _Len + ns;
_Grow(n) ;
_Copylr(str, _Len, pos, n);
_Setl (n);
}
return (*this);
o
Here is where the implementation pays the price for economical use of
storage. Copying a substring within a bit sequence is not eas)T, given
arbitrary bit offsets and an arbitrary length. How these interact with array
element boundaries is tricky, and optimizing to move whole array elements
wherever possible is even trickier. I simplified matters somewhat by defin-
ing two kinds of moves, copying bits from left to right or from right to left.
The calling functions must decide which flavor is safer, if source and
destination can possibly overlap.
462 Chapter 18
Figure 18. 14: II bitsins -- bitstring::insert(size_t, const bitstring&,
bitsins.c II size_t, size_t)
#include <bitstring>
return <*this);
o
bitscplr.c Figure 18.10 shows the file bitscplr.c, which defines the member
bitscprl.c function _Copylr. And Figure 18.11 shows the file bitscprl. c, which
defines the member function _Copyrl. Each moves either whole elements
(po % _Nb == 0), or partial elements which the function must shift and
pack into existing elements. And each must tidy up the least-significant
element affected by the move. Be warned that this code is delicate.
<bitstring> 463
Figure 18. 16: II bitsrep -- bitstring::replace(size_t size_t,
II const bitstring&, size_t, size_t)
bitsrep.c #include <bitstring>
bitsapp.c Six source files define similar member functions that edit the bit se-
bitsass.c quence controlled by a bitstring object. Figure 18.12 shows the file
bitsins.c bitsapp.c, which defines the member function append. Figure 18.13
bitsrem.c shows the file bitsass.c, which defines the member function assign.
bitsrep.c Figure 18.14 shows the file bitsins . c, which defines the member function
bitsresi.c insert. Figure 18.15 shows the file bitsrem. c, which defines the member
function remove. Figure 18.16 shows the file bitsrep. c, which defines the
member function replace. And Figure 18.17 shows the file bitsresi .c,
which defines the member function resize. All perform their edits in terms
of calls to the secret member functions _Copylr and _Copyrl.
bitsset.c Five more source files define small member functions that implement
bitsetl.c bit-manipulation operations. Figure 18.18 shows the file bitsset .c. It
bitsrese.c defines the member function set ( ), which sets all bits in the sequence.
bitstog.c Figure 18.19 shows the file bitssetl.c. It defines the member function
bitstogl.c set (size_t pO, _Bool val), which sets the bit at position pO to the value
val 1= o. Figure 18.20 shows the file hitsrese.c. It defines the member
function reset (), which clears all bits. Figure 18.21 shows the file hi ts-
tog. c. It defines the member function toggle ( ), which toggles all bits.
And Figure 18.22 shows the file bitstogl. c. It defines the member func-
tion toggle (size_t pO), which toggles the bit at position pO. All are
straightforward.
464 Chapter 18
Figure 18.17: II bitsresize -- bitstring::resize(size_t, _Boo1)
#inc1ude <bitstring>
bitsresi.c
const bitstring ones(bitstring::_Ones);
const bitstring zeros(bitstring::_Zeros);
return (val);
D
466 Chapter 18
Figure 18.24: II bitstost -- bitstring::to_string(size_t)
#include <bitstring>
bitstost.c
string bitstring::to_string() const
( II make a string from a bitstring
string str(_Len, reserve);
for (size_t pos = 0; pos < _Len; ++pos)
str += test(pos) ? '1' : 'O'i
return (str);
D
else
for (; i <= nw; ++i, pos += _Nb)
( II find leftmost zero bit
T x = _Ptr[i];
for (size_t i = pos; -x != 0;
x = x « 1 I 1, ++i)
if (!(x & MBB) && pO <= i && i < pf)
return (i);
return (NPOS);
o
bitscoun.c Two source files define small member functions that inspect each bit in
bitstost.c a sequence. Figure 18.23 shows the file bi tscoun. c. It defines the member
function count ( ) I which counts the number of set bits. The cute code
processes bits four at a time. And Figure 18.24 shows the file bitstost .c,
which defines the member function to_string. Converting to a string
object is eas~ given the member functions in both classes.
bitsfind.c Two member functions search for a given bit value in a sequence. Figu.re
bitsrfin.c 18.25 shows the file bit sf ind. c, which defines the member function find.
<bitstring> 467
else
for (; nw <= i; --i, pos -= _Nb)
{ II find rightmost zero bit
T x = _Ptr[i];
for (size_t j = pos; -x != 0;
x = x » 1 I MSB, --j)
if (!(x & 1) && pO <= j && j < pf)
return (j);
return (NPOS);
o
o
468 Chapter 18
Figure 18.28: II bitstest -- bitstring::test(size_t, int)
#include <bitstring>
bitstestoc
_Bool bitstring::test(size_t pO) const
{ II test one bit in a bitstring
if <_Len <= pO)
_Xran() ;
return «_Ptr[pO I _Nbl & (_T)l « (_Nb - 1 - pO % _Nb»
!= 0);
o
And Figure 18.26 shows the file bitsrfino c, which defines the member
function rfind. Both are much more elaborate than they have to be, in the
interest of improving performance over the obvious, simple code. Both
functions separate the logic for searching for a set bit or a clear bit.
bitseq.c The last three source files implement member functions that compare or
bitstest.c test bit seqeunces in various ways. Figure 18.27 shows the file bitseq. c. It
bitsanyoc overloads operator== for comparing two bitstring objects. Figure 18.28
shows the file bitstest c. It defines the member function test (size_t
0
pO), which tests the bit at position pO. Finall)', Figure 18.29 shows tIle file
bitsanyoc. It defines the mmeber function any, which tests whether any
bit in the sequence is set. As usual, these functions process a whole array
element at a time whenever possible.
Exercises
Exercise 18.1 Does it make sense to define bitstring member functions similar to the
string member functions find_first_of and find_last_not_of? If so,
describe how they might work.
Exercise 18.2 Define a class that represents an array of eleven-bit data samples, using a
bitstring object to manage storage for the array efficiently. What member
functions should your class have to be reasonably complete?
Exercise 18.3 Write a function that exchanges two bit sequences. (Hint: consider the
EXCLUSIVE OR operation.)
Exercise 18.4 Write a function that reverses the bits in a sequence controlled by a bit-
string object.
Exercise 18.5 Add a member function to class bitstring to find the leftmost occurrence
of an argument bit sequence within the controlled bit sequence. What is a
good name for the function? What parameters should it have?
Exercise 18.6 [Harder] Combine the classes string and bitstring so that you retain all
the functionality of both classes. Aside from storage efficienc)T, the class
should also behave the same as class bitstring if all the elements of the
sequence are restricted to the values zero and one.
Exercise 18.7 [Very hard] Define and implement a bitstring class that has no limit on
the length of a bit sequence.
470 Chapter 18
Figure 18.30: II test <bitstring>
#include <assert.h>
tbitstri.c
#include <bitstring>
Part 1 of 2 #include <iostream>
int main()
{ II test basic workings of bitstring definitions
string str(lIllll0l0l");
bitstring xO, xl(Ox1230, 1), x2(xl), x3(xl, 1, 8);
bitstring x4(str), xS(str, 4, 3);
assert(xO.length() == 0 &:&: xO.to_string() == .... );
assert(xl.length() == 13
&:&: xl.to_string() "1001000110000");
assert (x2.count() == 4
&:&: x2. to_string () "1001000110000 II) ;
assert (x3. to_string () II 00100011 11 ) ;
assert(x4.to_string() == 1111110101");
assert (xS.to_string() == 11010");
II test assigns and appends
xO = xl, assert (xO.to_string() ==
111001000110000");
xO.assign(x3), assert (xO.to_string() ==
"00100011");
xO.assign(x3, 1), assert (xO.to_string() ==
nOl000ll l1 ) ;
xO.assign(x3, 2, 3), assert(xO.to_string() ==
"100 11 );
xO += xS, assert (xO.to_string() ==11100010");
xO.append(xS), assert (xO.to_string() ==
11100010010 11 );
xO.append(xS, 1), assert(xO.to_string() ==
"10001001010");
xO.append(xS, 2, 3);
assert (xO.to_string() == 11100010010100");
xO.assign(xO, 3, 4), assert(xO.to_string() ==
110100 11 );
assert«xl + xl + xl).to_string() ==
"1001000110000"
111001000110000" 111001000110000 11 );
II test logic
xO = x3;
xO 1= xS, assert (xO.to_string() == 1101100011 11 );
assert «x3 1 xS). to_string () == ..
01100011") ;
assert«x3 1 str).to_string() =="11110111 11 );
xO &:= x4, assert(xO.to_string() == 1101100001");
assert«x3 &: x4).to_string() =="00100001");
xO A= x2, assert (xO.to_string() == "1111000010000");
assert«x4 A xS).to_string() =="10110101");
xO «= 2, assert(xO.to_string() == "1100001000000");
assert«x4 « 3).to_string() =="10101000");
xO »= 4, assert(xO.to_string() == "0000110000100");
assert«x4 » 2).to_string() =="00111101 11 );
assert«-x3).to_string() == 1111011100");
assert«(xl + xl + xl) A xl) ==(xl + xl + xl) « 13);
II test inserts, removes, replaces
xO = xS;
xO.insert(l, xS), assert (xO.to_string() "001010");
xO.insert(l, xS, 1), assert (xO.to_string() ==
1101001010");
xO.insert(l, xS, 1, 1);
assert(xO.to_string() == "011001010");
xO.remove(7), assert (xO.to_string() ==
110110010");
xO.remove(2, 3), assert(xO.to_string() == 110110");
xO.replace(2, 1, xS), assert(xO.to_string() ==
11010100");
<bitstring> 471
Background
So far, I have discussed three different classes that manipulate varying-
length sequences:
string - string, which controls sequences of single-byte characters (see Chapter
15: <string»
wstring - wstring, which controls sequences of wide characters (see Chapter 16:
<wstring»
bitstring - bitstring, which controls sequences of bits (see Chapter 15: <bit-
string»
You can argue that each of these kinds of sequences deserves special
handling because it is likely to be widely used in C++ programs. Moreover,
each of these presents special opportunities for improving performance or
space efficiency. But after a while, making yet another string-like class gets
old. You'd like to start recycling the common design features, and the
common code.
value The header <dynarray> addresses this desire. It defines the template
semantics class dynarray<T>, which controls a sequence of elements of type T. Here,
T can be any type with value selnantics. Put simply, that means you can freely
copy about objects of such a type. There is nothing magic about where the
object resides. Put more formall)!, the type T must have:
- a default constructor
- a copy constructor
- an assignment operator
- a destructor
all with the expected behavior. All the scalar types (such as char, float, and
pointers) meet these requirements. So do many Standard C++ library
classes such as ios and double_complex. (See Chapter 6: <ios> and
Chapter 21: <complex>.) Some do not, however, such as class streambuf.
(See Chapter 7: <streambuf>.)
If you want to control varying-length sequences of objects that don't
meet these requirements, do not despair. That's the topic of the next chapter.
(See Chapter 20: <ptrdynarray>.)
474 Chapter 19
Footnotes:
Footnotes 116) Objects that cannot tolerate this uncertainty, or that fail to meet the stated
requirements, can sometimes be organized into dynamic arrays through the
intermediary of an object of class ptrdynarray.
117) Presumably, this operation occurs with no actual copying of array elements.
Future Directions
dyn_array The name of the class has been changed to dyn_array.
base The member function dynarray<T>: : base has been changed to dynar-
ray<T>: :data.
Standard As I mentioned earlier, the Standard Template Library (STL) will almost
Template certainly replace the header <dynarray>. (See page 427.) A combination of
Library more general templates from STL is an adequate substitute for an explicit
template class dynarray<T>. Nevertheless, I provide here a full discussion
of the header <dynarray>, for the reasons I outlined earlier.
Using <dynarray>
You include the header <dynarray> to make use of the template class
dynarray<T>. Here, T is a type with value semantics, as described on page
473. Objects of such a template class let you represent and manipulate
in-memory sequences that vary dynamically in length. The dynarray<T>
object allocates and frees storage as needed to accommodate changes in
length of the controlled sequence.
construction To construct a dynarray<T> object xO with an initially empty sequence,
you can simply write:
dynarray<T> xO;
length Each object x reports the length of its sequence with the call x.length () .
reserve It also reports its reserve size with the call x. reserve ( ). For a newly
constructed dynarray<T> object, the reserve size suggests how much stor-
age to initially allocate for the sequence. You can, for example, specify an
initial reserve size of 100 elements by writing:
<dynarray> 481
capacity dynarray<T> xO(100, reserve);
For an object with a non-empty sequence, the reserve size suggests how
much storage is currently available to hold the sequence of type T. Thus,
xO • length () <= xO. reserve () at all times. As a rule, you can ignore the
reserve size. The implementation will guess it for you. (But see the discus-
sion on page 279.)
To construct a dynarray<T> and define its initial sequence, you have a
number of choices. To give concrete examples, I show the explicit instan-
tiation dynarray<Char>, given
class Char {
public:
Char(char c = 'X')
: ch(c) {};
private:
char chI
};
Char b('B'), xxxx[4];
In each case below, the comment shows the character values stored in the
elements of the resulting sequence. You can write:
dynarray<Char> xl(5, default_size); II XXXXX
dynarray<Char> x2(b); II B
dynarray<Char> x3(b, 5); II BBBBB
dynarray<Char> x4(xxxx, 2); II XX
dynarray<Char> x5(x3); II BBBBB
notation Class dynarray<T> supports several combinations of argument types
for many functions that construct or manipulate such objects. For brevity,
I define here a terse and uniform style for writing various argument
combinations. The resultant argument is:
• a single element (of type T), with the argument t
• a repetition of elements, with the arguments t, rep
• a pointer to a single element, with the argument s
• an array of elements with a specified length, with the arguments s, n
• an object of class dynarray<T>, with the argument x
With this notation, I can more quickly summarize all the ways you can
perform various operations involving dynarray<T> objects. Here, for ex-
ample, is a summary of most of the constructors I showed above:
dynarray<T> x2(t);
dynarray<T> x3(t, rep);
dynarray<T> x4(s, n);
dynarray<T> x5(x);
A number of member functions alter the sequence controlled by a
dynarray<T> object xO.
To assign a new sequence, in place of any existing one:
assign xO.assign(t)
xO.assign(t, rep)
xO.assign(s)
xO.assign(s, n)
xO.assign(x) xO x
482 Chapter 19
To append to an existing sequence:
append xO. append (t) xO += t
xO. append (t, rep)
xO.append(s)
xO.append(s, n)
xO.append(x) xO += x
To insert before position px in an existing sequence:
insert xO.insert(px, t)
xO.insert(px, t, rep)
xO.insert(px, s)
xO.insert(px, s, n)
xO.insert(px, x)
You can remove a subsequence from a sequence in various ways:
remove xO. remove () II remove all elements
xO.rem.ove(px) II remove all from px to end
xO.remove(px, n) II remove at most n beginning at px
You can establish a new length len for a sequence. If the new length is
greater than the existing length, the sequence is padded as stated in the
comment below:
swap You can swap the controlled sequences with that controlled by another
dynarray<T> object x:
xO.swap(x)
This call is presumably much faster than the conventional idiom:
dynarray<T> xt(xO);
xO = x, x = xt;
You can establish a new length len for a sequence. If the new length is
greater than the existing length, the sequence is padded as stated in the
comment below:
resize xO.resize(len) II pad to len with default constructor
xO.resize(len, t) II pad to len by repeating t
As a final alteration, you can store a new object t in the element at
position px two different ways:
put_at xO.put_at(px, t)
xO[px] = t
operator [ ] Be warned, however, that the subscript notation is more delicate. It returns
a reference that can be invalidated by all sorts of subsequent operations on
xO. Use it only in a very localized context, as above.
Another set of functions lets you obtain part or all of the sequence
controlled by a dynarray<T> object. The compaions to the functions imme-
diately above are:
get_at xO.get_at(px)
xO[px]
operator [] both of which return the object stored at position px.
You can get a pointer to the beginning of the entire sequence by calling:
base xO •base ( )
<dynarray> 483
The same caveats apply as for the subscript operator, above. Use the pointer
to do any direct accessing of the sequence quickl)T, before you perform any
subsequent operations on xO. The pointer may well become invalid.
sub_array You can copy a subsequence into another dynarray<T> object x:
xO.sub_array(x, px) II copy from px to end
xO.sub_array(x, px, n) II copy at most n beginning at px
Note that these calls return a reference to x.
Finally, some functions each construct a dynarray<T> object to return as
the value of the function, leaving the object xO unaltered. You can, for
example, construct a dynarray<T> object that appends the sequences de-
fined by two operands:
operator+ x + t
t + x
x + xO
Implementing <dynarray>
dynarray Figure 19.1 shows the file dynarray, which implements the standard
header <dynarray>. It defines the template class dynarray<T> and a few
related template functions. All template functions appear within the header
as inline definitions, as I discussed much earlier. (See page 253.) Neverthe-
less, you will find that not all translators will accept this header unmodified.
(See page 430.)
The template class dynarray<T> also defines five secret protected mem-
ber functions:
_Grow - _Grow, which alters the storage reserved for the sequence
_Tidy - _Tidy, which initializes (_Tidy ( » the member objects at construction
time or discards any sequence (_Tidy ( 1» and reinitializes the member
objects
_Xinv - _Xinv, which reports an invalid-argument error
_Xlen - _Xlen, which reports a length error
_Xran - _Xran, which reports a range error
For a discussion of the exceptions associated with invalid-argument,
length, and range errors, see Chapter 3: <exception>. As you can see, all
of these functions are used throughout the member function definitions.
I have added a default argument to several member functions. These are
functions that accept a pointer to a sequence and a length (canst T*,
size_t). Each has a companion function that accepts a reference to an
element and a repetition count (canst T&, size_t = 1). To enable the
companion function to call the former version, I add to the former a pointer
increment parameter (size_t _D = 1). Thus, the function steps by one to
walk a sequence, and by zero to repeat a single element.
None of the functions is otherwise complex. For hints on what individual
member functions do, see their analogs in class string.
484 Chapter 19
Figure 19.1: II dynarray standard header
#ifndef _OYNARRAY_
dynarray
#define _OYNARRAY_
Part 1 of 3 #include <defines>
II template class dynarray
template<class _T> class dynarray {
public:
dynarray()
{_Tidy(); }
dynarray(size_t _N, capacity _C)
{_Tidy(), _Res = _N;
if (_C == default_size)
_Grow(_N); }
dynarray(const dynarray<_T>& _X)
{_Tidy(), _Grow(_X.length(»;
for (size_t _I = 0; _I < _Len; ++_1)
_Ptr[_I] = _Xl_I]; }
dynarray(const _T& _X, size_t _N = 1)
{_Tidy(), _Grow (_N, &_X); }
dynarray(const _T *_S, size_t _N)
{if (_S == 0)
_Xinv() ;
_Tidy(), assign(_S, _N); }
-dynarray ( )
{_Tidy(l); }
dynarray<_T>& operator=(const dynarray<_T>& _R)
{return (this == &_R ? *this
: assign(_R.base(), _R.length(»); }
dynarray<_T>& operator+=(const dynarray<_T>& _R}
{return (append(_R.base(), _R.length(}»; }
dynarray<_T>& operator+=(const _T& _R)
{return (append(_R»; }
dynarray<_T>& append(const _T& _X, size_t _N 1)
{return (append (&_X, _H, 0»; }
dynarray<_T>& append(const _T *_8, size_t _N 1,
size_t _0 = 1)
{if (NPOS - _Len <= _H)
_Xlen() ;
size_t _I = _Len;
for (_Grow(_N += _I); _I < _N; ++_1, _S += _0)
_Ptr[_I] = *_S;
return (*this); }
dynarray<_T>& assign(const _T& _X, size_t _N 1)
{return (assign(&_X, _H, 0»;
dynarray<_T>& assign(const _T *_8, size_t _N 1,
size_t _0 = 1)
{_Grow (_N, 0, 1);
for (size_t _I = 0; _I < _N; ++_1, _S += _D)
_Ptr[_I] = *_S;
return <*this); }
dynarray<_T>& insert(size_t _P, canst dynarray<_T>& _X)
{return (insert(_P, _X.base(), _X.length(»); }
dynarray<_T>& insert(size_t _P, const _T& _X, size_t _N 1)
{return (insert(_P, &_X, _N, 0»; }
dynarray<_T>& insert(size_t _P, const _T *_S, size_t _N 1,
<dynarray> 485
Continuing size_t _D = 1)
{if (_Len < _P)
dynarray _Xran() ;
Part 2 of 3 if (HPOS - _Len <= _H)
_Xlen() ;
if (0 < _H)
{size_t _I = _Len - _PI
for (_Grow(_H + _Len); 0 < _I; )
--_I, _Ptr[_P + _H + _Il = _Ptr[_P + _Il;
for (_I = 0; _I < _H; ++_I, _8 += _D)
_Ptr[_P + _Il *_8;
return (*this); }
dynarray<_T>& remove(size_t _P = 0, size_t _H HPOS)
{if (_Len < _P)
_Xran() ;
if (_Len - _P < _H)
_H = _Len - _PI
if (0 < _H)
{size_t _M = _Len - _P - _H;
for (size_t _I = 0; _I < _M; ++_I)
_Ptr[_P + _Il = _Ptr[_P + _I + _Hl;
_Grow(_Len - _H); }
return (*this); }
dynarray<_T>& sub_array(dynarraY<_T>& _X, size_t _P,
size_t _H = HPOS)
{if (_Len < _P)
_Xran() ;
if (_Len - _P < _H)
_H = _Len - _PI
return (this == &_X ? (remove(_P + _H), remove(O, _P»
: _X.assign(&_Ptr[_Pl, _H»;
void swap(dynarray<_T>& _X)
{_T *_Tp = _ptr; _Ptr = _X._ptr, _X._ptr _Tp;
size_t _Tl = _Len; _Len = _X._Len, _X._Len _Tl;
size_t _Tr = _Res; _Res = _X._Res, _X._Res = _Tr;
const _T& get_at(size_t _I) const
{if (_Len <= _I)
_Xran{) ;
return (_Ptr[_I]);
void put_at(size_t _I, const _T& _X)
{if (_Len <= _I)
_Xran();
_Ptr[_I] = _X; }
_T& operator[] (size_t _I)
{return (_Ptr[_I]); }
const _T& operator[] {size_t _I) const
{return (_Ptr[_I]); }
_T *base()
{return (_Len 1= 0 ? _ptr 0);
const _T *base() const
{return (_Len 1= 0 ? _ptr 0);
size_t length() const
{return (_Len); }
void resize(size_t _H)
{_Grow (_H, 0, 1);
486 Chapter 19
Continuing void resize(size_t _N, const _T& _X)
{_Grow (_N, &_X, 1);
dynarray
size_t reserve() const
Part 3 of 3 {return (_Res); }
void reserve(size_t _R)
{if (_ptr == 0)
_Res = _R; }
private:
void _Grow(size_t _N, const T *_8 = 0, _Bool _Trim 0)
{size_t _Os = _ptr == 0 ? 0 : _Res;
if (_N == 0)
{if (_Trim)
_Tidy(l); }
else if (_N _Os I I _N < _Os && !_Trim)
Testing <dynarray>
tdynarra.c Figure 19.2 shows the file tdynarra.c. It defines the simple class Char
that stores a single character. The default constructor for this class stores
'x'. It also overloads operator== and operator+ for various parameter
combinations that all include at least one parameter of type const dynar-
ray<Char>&. Some implementations need tIle three explicit declarations of
operator+ instantiated for dynarray<Char>. Others may not.
With this scaffolding, the function defines several objects of type dynar-
ray<Char> and exercises at least the obvious features of the template class.
If all goes well, the program prints:
SUCCESS testing <dynarray>
and takes a normal exit.
Exercises
Exercise 19.1 Does the definition of the template class dynarray<T> guarantee that the
sequence is always represented in contiguous storage?
Exercise 19.2 How would you instantiate the template class dynarray<T> to control a
sequence of bits?
Exercise 19.3 What is the meaning of the instantiation dynarray<const int>? Is it
permitted? Is it useful? Does the code in file dynarray survive such an
instantiation?
Exercise 19.4 Compare the performance of objects of class string with that for objects
of class dynarray<char>.
Exercise 19.5 Implement the class double_array that has the same functionality as
dynarray<double>.
Exercise 19.6 Compare the performance of objects of class double_array, from the
previous exercise, with that for objects of class dynarray<double>.
Exercise 19.7 [Harder] Rewrite the definition of template class dynarray<T> to provide
predictable calling patterns for the copy constructor, assignment operator,
and destructor supplied by T.
Exercise 19.8 [Very hard] Rewrite the definition of template class dynarray<T> for your
implementation so that it generates the most optimal code for an arbitrary
type T with value semantics.
488 Chapter 19
Figure 19.2: II test <dynarray>
#include <assert.h>
tdynarra.c
#include <dynarray>
Part 1 of 2 #include <iostream>
class Char {
public:
Char(char c = 'X')
: ch(c) {};
char val() const
{return (ch); }
_Bool operator==(const Char&: c) const
{return (ch == c.val(»; }
private:
char ch,
};
int main ( )
{ II test basic workings of dynarray definitions
Char cO, ca('a'), cb('b'), c3[3];
dynarray<Char> sl, s2(30, reserve), s3(4, default_size);
dynarray<Char> s4(s3), s5(ca), s6(cb, 10);
const dynarray<Char> s7(c3, 3);
assert(sl.length() 0);
sl.reserve(10);
assert (sl.length() 0 && sl.reserve() == 10);
assert (s2.length() 0 && s2.reserve() == 30);
assert(s3.1ength() 4 && s3 == IIXXXX II );
assert(s4.1ength(} 4 && s4 == IIXXXX");
assert (85 • length ( ) 1 &&: 85 [0] == ca);
assert(s6.length() 10 &&: 86 == "bbbbbbbbbb");
assert(s7.length() 3 && s7 == "XXXII);
sl.resize(2), assert(sl == IIXX II );
sl.resize(4, cal, assert(sl == "XXaa ll ) ;
sl.resize(3), assert(sl == IIXXa ll ) ;
II test assigns
sl = s5, assert(sl == lIa ll ) ;
sl = sl, assert(sl == "a");
sl.assign(cb), assert(sl == IIb");
sl.assign(cb, 5), assert(sl == IIbbbbb");
sl.assign(&:cb), assert(sl == "b");
sl.assign(c3, 3), assert(sl == IIXXX");
<dynarray> 489
Continuing II test appends
s1 = ss, s1 += ss, assert(s1 == "aa");
tdynarra.c
s1 += cb, assert(s1 == "aab");
Part 2 of 2 s1.append(cb), assert(s1 == "aabb");
s1.append(cb, 5), assert(s1 == "aabbbbbbb ll ) ;
s1.append(&ca), assert(s1 == "aabbbbbbba");
s1.append(c3, 3), assert(s1 == lIaabbbbbbbaXXX");
s1 s4 + ss, assert(s1 "XXXXa n );
s1 ca + ss, assert(s1 == naa");
s1 s4 + cb, assert(s1 == nXXXXbn);
II test inserts, removes, etc.
s1 = s4, s1.insert(1, s5), assert(s1 == nXaXXX n );
s1.insert(2, cb), assert(s1 == IIXabXXX n );
s1.insert(0, ca, 3), assert(s1 == "aaaXabXXX");
s1.insert(9, &ca), assert(s1 == "aaaXabXXXa ll ) ;
s1.insert(1, c3, 3), assert(s1 == "aXXXaaXabXXXa ll ) ;
s1.remove(7), assert(s1 == "aXXXaax");
s1.remove(2, 1), assert(s1 == "aXXaaX");
s1.remove(), assert(s1 == "");
s6.sub_array(s1, 2);
assert(s6 == "bbbbbbbbbb" && s1 == IIbbbbbbbb");
s1.sub_array(s1, 3, 2), assert(s1 == "bb n );
s1.swap(s3), assert(s1 == "XXXX" && s3 == nbb");
II test access functions
s1 = s7, assert(s1[0] == cO);
s1[0] = ca, assert(s1[0] == cal;
s1.put_at(1, cb), assert(s1.get_at(1) == cb);
assert(s1.base() [1] == cb && s7.base() [2] == cO);
cout « "SUCCESS testing <dynarray>" « endl;
return (0);
o
Chapter 20: <ptrdynarray>
Background
The template class dynarray<T>, described in the previous chapter, has
an important requirement. The parameter type T must have value seman-
tics, so that objects of that type can be freely copied. This requirement is
met by all scalar objects, but not necessarily for a class you define. If you
want to organize objects of an arbitrary type T into varying-length se-
quences, all is not lost. You can construct a sequence of pointers to such
objects, as dynarray<T *>. Then, only the pointers get copied about.
The header <ptrdynarray> supplies a bit of help in managing pointer
sequences. It defines the template class ptrdynarray<T>, which has the
public base class dynarray<void *>. The derived class then hides all
member functions in the base class with versions tailored specifically for
pointers to T. The template class ptrdynarray<T> thus controls a sequence
of pointers to an object type T, maintained as a sequence of pointers to void.
Future Directions
The name of the class has been changed to ptr_dyn_array.
base The member function ptrdynarray<T>: :base has been changed to
ptrdynarray<T>: : data.
Standard The header <ptrdynarray> is the last of the candidates in this book for
Template replacement by the Standard Template Library (STL). (See page 427.) A
Library combination of more general templates from STL is an adequate substitute
for an explicit template class ptrdynarray<T>. Once again, however, I
provide here a full discussion of the header <ptrdynarray>, for the reasons
I outlined earlier.
Using <ptrdynarray>
You include the header <ptrdynarray> to make use of the template class
ptrdynarray<T>. Here, T is an arbitrary object type. Objects of such a
template class let you represent and manipulate in-memory sequences, of
pointers to T, that vary dynamically in length. The ptrdynarray<T> object
allocates and frees storage as needed to accommodate changes in length of
the controlled sequence. For a description of how to use a ptrdynarray<T>
object, see the previous chapter. The only difference is that every reference
to T, in the description of class dynarray<T>, is replaced by a pointer to T.
Implementing <ptrdynarray>
ptrdynar Figure 20.1 shows the file ptrdynar, which implements the standard
header <ptrdynarray>. It defines the template class ptrdynarray<T> and
a few related template functions. All template functions appear within the
header as inline definitions, as I discussed much earlier. (See page 253.) In
this particular case, such an approach is eminently sensible.
Testing <ptrdynarray>
tptrdyna.c Figure 20.2 shows the file tptrdyna.c, a simple variant of the file
tdynarra. c in the previous chapter. (See page 488.) It defines the simple
class Char that stores a single character. The default constructor for this class
stores 'x'. It also overloads operator== and operator+ for various pa-
rameter combinations that all include at least one parameter of type const
dynarray<Char>&. With this scaffolding, the function defines several ob-
jects of typeptrdynarray<Char> and exercises at least the obvious features
of the template class.
If all goes well, the program prints:
SUCCESS testing <ptrdynarray>
and takes a normal exit.
496 Chapter 20
Figure 20. 1: II ptrdynarray standard header
#ifndef _PTRDYNARRAY_
ptrdynar.c
#define _PTRDYNARRAY_
Part 1 of 2 #include <dynarray>
II template class ptrdynarray
template<class _T> class ptrdynarray
: public dynarray<void *> {
public:
ptrdynarray ( )
: dynarray<void *>() {}
ptrdynarray(size_t _N, capacity _C)
: dynarray<void *>(_N, _C) {}
ptrdynarray(const ptrdynarray<_T>& _X)
: dynarray<void *>(_X) {}
ptrdynarray(_T *_X, size_t _N = 1)
: dynarray<void *>«void *)_X, _N) {}
ptrdynarray(_T **_S, size_t _N = 1)
: dynarray<void *>«void **)_8, _N) {}
ptrdynarray<_T>& operator=(const ptrdynarray<_T>& _R)
{return «ptrdynarray<_T>&)dynarray<void *>::
operator=«const dynarray<void *>&)_R»; }
ptrdynarray<_T>& operator+=(const ptrdynarray<_T>& _R)
{return «ptrdynarray<_T>&)dynarray<void *>::
operator+=«const dynarray<void *>&)_R»;
ptrdynarray<_T>& operator+=(_T *_R)
{return «ptrdynarray<_T>&)dynarray<void· *>::
operator+=«void *)_R»; }
ptrdynarray<_T>& append(_T *_X, size_t _N = 1)
{return «ptrdynarray<_T>&)dynarray<void *>::
append«void *)_X, _N»; }
ptrdynarray<_T>& append(_T **_S, size_t _N = 1)
{return «ptrdynarray<_T>&)dynarray<void *>::
append«void **)_8, _N»; }
ptrdynarray<_T>& assign(_T *_X, size_t _N = 1)
{return «ptrdynarray<_T>&)dynarray<void *>::
assign«void *)_X, _N»; }
ptrdynarray<_T>& assign(_T **_S, size_t _N = 1)
{return «ptrdynarray<_T>&)dynarray<void *>::
assign({void **)_8, _N»; }
ptrdynarray<_T>& insert(size_t _P,
const ptrdynarray<_T>& _X)
{return «ptrdynarray<_T>&)dynarray<void *>::
insert (_P, (const dynarray<void *>&)_X»; }
ptrdynarray<_T>& insert(size_t _P, _T *_X, size_t _N 1)
{return «ptrdynarray<_T>&)dynarray<void *>::
insert (_P, (void *)_X, _N»; }
ptrdynarray<_T>& insert(size_t _P, _T **_8, size_t _N 1)
{return «ptrdynarray<_T>&)dynarray<void *>::
insert (_P, (void **)_S, _N»; }
ptrdynarray<_T>& remove(size_t _P = 0, size_t _N = NPOS)
{return «ptrdynarray<_T>&)dynarray<void *>::
remove (_P, _N»; }
ptrdynarray<_T>& sub_array(ptrdynarray<_T>& _X, size_t _P,
size_t _N = NPOS)
{return «ptrdynarray<_T>&)dynarray<void *>::
<ptrdynarray> 497
Continuing sub_array (_X, _P, _N»;
void swap(ptrdynarray<_T>& _X)
ptrdynar
(dynarray<void *>::swap(_X);
Part 2 of 2 _T* get_at(size_t _P) const
{return «_T *)dynarray<void *>::get_at(_P»;
void put_at(size_t _P, _T *_X)
{dynarray<void *>::put_at(_P, (void *)_X); }
_T *& operator[] (size_t _P)
{return «_T *&)dynarray<void *>::operator[] (_P»;
_T *const& operator[] (size_t _P) const
{return «_T *&)dynarray<void *>::operator[] (_P»;
_T **base ()
{return «_T **)dynarray<void *>::base(»;
const _T **base() const
{return «_T **)dynarray<void *>::base(»;
size_t length() const
{return (dynarray<void *>::length(»;
void resize(size_t _N)
(dynarray<void *>::resize(_N); }
void resize(size_t _N, _T *_X)
{dynarray<void *>::resize(_N, (void *)_X);
size_t reserve() const
{return (dynarray<void *>::reserve(»;
void reserve(size_t _N)
(dynarray<void *>::reserve(_N);
};
II template operators
template<class _T> ptrdynarray<_T>
operator+(const ptrdynarray<_T>& _L,
const ptrdynarray<_T>& _R)
(return (ptrdynarray<_T> (_L) += _R);
template<class _T> ptrdynarray<_T>
operator+(const ptrdynarray<_T>& _L, _T *_R)
(return (ptrdynarray<_T> (_L) += _R); }
template<class _T> ptrdynarray<_T>
operator+(_T *_L, const ptrdynarray<_T>& _R)
(return (ptrdynarray<_T> (_L) += _R); }
#endif o
Exercises
Exercise 20.1 Is there ever any difference in effect between, say; dynarray<int *> and
ptrdynarray<int>? If so, what is the difference? How might it affect
program execution?
Exercise 20.2 Say you intend to instantiate ptrdynarray<T> for many different types. Is
this approach better or worse, in any way, than instantiating dynarray<T>
for the corresponding pointer types instead?
Exercise 20.3 Is it permissible to instantiate ptrdynarray<fvoid_t >? (For the definition
of fvoid_t, see Chapter 2: <defines>.) If not, how would you achieve the
desired effect?
498 Chapter 20
Exercise 20.4 Why might you want to maintain a varying-length sequence of streambuf
objects?
Exercise 20.5 [Harder] Rewrite the definition of template class ptrdynarray<T> so that
it accepts the same parameter types (such as T&: and T *) as dynarray<T>
and converts them as needed to corresponding pointer types (such as T *
and T **).
Exercise 20.6 [Very hard] Rewrite the definition of template class dynarray<T> for your
implementation so that it adapts as needed to a class T that doesn't have
value semantics.
class Char {
public:
Char(char c = 'X')
: ch(c) {};
char val () const
{return (ch); }
_Bool operator==(const Char& c) const
{return (ch == c.val(»; }
private:
char ch,
};
int maine)
( II test basic workings of ptrdynarray definitions
static Char cO, ca('a'), cb('b'), c3[3];
static Char *pc3[] = {&ca, &cb, &cO};
ptrdynarray<Char> sl, s2(30, reserve), s3(4, default_size);
ptrdynarray<Char> s4(s3), s5(&ca), s6(&cb, 10);
const ptrdynarray<Char> s7(pc3, 3);
assert(sl.length() 0);
sl.reserve(10);
assert (sl.length() a && sl.reserve() 10);
assert (s2.1ength() a && s2.reserve() 30);
<ptrdynarray> 499
Continuing assert(s3.1ength() 4);
assert (s4.1ength() 4);
tptrdyna.c
assert (s5 • length ( ) 1 &:&: s5 [0] &:ca); ==
Part 2 of 2 assert (s6.1ength() 10 &:&: s6 ==
"bbbbbbbbbb");
assert(s7.1ength() 3 &:&: s7 labX"); ==
sl.resize(2), sl[O] = &:cO, sl[l] =
&:cO, assert(sl "XX II ) ;
sl.resize(4, &:ca), assert(sl IXXaa");==
sl.resize(3), assert(sl == "XXa lf ) ;
II test assigns
sl = s5, assert(sl ==
"a");
sl = sl, assert(sl ==
"a");
sl.assign(&:cb), assert(sl lib");==
sl.assign(&:cb, 5), assert(sl ==
"bbbbb ll ) ;
sl.assign(pc3), assert(sl naif);==
sl.assign(pc3, 3), assert(sl "abXn);
II test appends
sl = s5, sl += s5, assert(sl naan); ==
sl += &:cb, assert(sl == naab n );
sl.append(&:cb), assert(sl ==
lIaabb");
sl.append(&:cb, 5), assert(sl ==
naabbbbbbb ll ) ;
sl.append(pc3), assert(sl ==
naabbbbbbba n );
sl.append(pc3, 3), assert(sl ==
naabbbbbbbaabX");
sl s6 + s7, assert(sl ==
IbbbbbbbbbbabX");
sl &:ca + s7, assert(s1 ==
laabX");
sl s7 + &:cb, assert(s1 == "abXbIl);
II test inserts, removes, etc.
sl s7, sl.insert(l, s7), assert(sl "aabXbXn); ==
sl.insert(2, &:cb), assert(sl ==
laabbXbX");
sl.insert(O, &:ca, 3), assert(sl ==
lIaaaaabbXbX");
sl.insert(10, pc3), assert(sl == lIaaaaabbXbXa");
sl.insert(l, pc3, 3), assert(sl ==
"aabXaaaabbXbXa ll ) ;
sl.remove(7), assert(sl ==
laabXaaa");
sl.remove(2, 1), assert(sl ==
laaXaaa");
sl.remove(), assert(sl == 1111);
s6.sub_array(sl, 2);
assert(s6 == IIbbbbbbbbbb" &:&: sl ==
"bbbbbbbb");
sl.sub_array(sl, 3, 2), assert(sl "bb"); ==
sl.swap(s6), assert(sl ==
IIbbbbbbbbbb" &:&: s6 IIbb"); ==
II test access functions
sl = s7, assert(sl[O] ==
&:ca);
sl[O] = &:cb, assert(sl[O] == &:cb);
sl.put_at(l, &:ca), assert(sl.get_at(l) &:ca); ==
assert(sl.base()[l] ==
&:ca &:&: s7.base() [2] &:cO); ==
cout « "SUCCESS testing <ptrdynarray>" « end1;
return (0);
o
SOC) Chapter 20
Chapter 21: <complex>
Background
Encapsulating complex arithmetic is one of the earliest applications for
C++. One of the first libraries supplied with a C++ translator supported
iostream-style input and output, complex arithmetic, and little else. Since
those early days, it has become traditional to provide at least minimal
support for making complex look more like a builtin type in C++.
complex.h Widespread current practice is to supply a header, such as complex.h,
that defines at least a complex class based on type double. Addition, sub-
traction, and negation make easy inline functions. Multiplication and divi-
sion are more involved, but still easy enough to supply: Other math func-
tions are considerably harder. Support for them varies considerably.
Indeed, it is hard to find a better excuse for hiding implementation
details of a class, or for overloading arithmetic operators, than providing
complex arithmetic. A number of operators have well established meanings
for complex values, dating back to the earliest days of FORTRAN. After
integer and floating-poillt values, complex values probably have the richest
culture in computer programming. Many scientists and engineers still
bemoan the loss of complex data types in later languages such as Pascal
and C. In a very real (and positive) sense, C++ allows C programs to look
more like FORTRAN.
The header <complex> is rather more ambitious than the typical header
<complex. h> of existing practice. It defines three classes, one for each of
the three floating-point types of Standard C:
• float_complex, based on float
• double_complex, based on double
• long_double_complex, based on long double
Cartesian The member objects are hidden inside each of these classes, as usual. In
form principle, the actual representation is equally hidden. But the functions that
access stored values deliver only the Cartesian components of a complex
value. Cartesian form represents a complex value as the pair (x, y) repre-
senting the sum x + i*y. Here, i is the notorious square root of -I, the unit
vector along the imaginary axis in the complex plane. Thus, the real com-
ponent is x and the imaginary component is y.
502 Chapter 21
polar Another way to represent complex values is with polar components.
form Polar form represents a complex value as the pair (p, 8) representing the
product p*exp(i*8). Thus, the magnitude is p and the argument (or phase angle)
is 8. Put simpl)', Cartesian form is far more convenient for addition and
subtraction, while polar form is sometimes more convenient for multipli-
cation and division. On balance, however, Cartesian form is generally more
convenient.
Beyond this brief introduction, I won't even try to explain all the mys-
teries of complex mathematics. Nor will I make a case for why you might
want to use it. If you're familiar with complex math, you know it is often
useful in its own right. It also models well many processes of interest to
scientists and engineers, such as time-varying voltages and motion in two
dimensions. But if you don't already know how you might use complex
arithmetic, here is not the place for me to try to teach it.
Future Directions
Strong sentiment exists for using templates to capture the parallelism
between the three complex arithmetic classes defined in <complex>. Un-
fortunately, template technology does not yet provide an easy way to
handle the asymmetric conversion rules that many programmers find
desirable for these classes.
Using <complex>
You include the header <complex> to make use of any of the three classes
float_complex, double_complex, or long_double_complex. Objects of
any of these classes let you represent complex values to a given floating-
point precision. Numerous functions, overloaded for all three classes,
perform many of the mathematical operations defined for complex values.
_STD_COMPLEX The header defines the macro _STD_COMPLEX as a reassurance. Presum-
abl)T, you can write code such as:
#if !defined( __STD_COMPLEX)
#error WRONG COMPLEX LiBRARY
#endif
The program should fail to translate if the implementation supplies a
nonconforming version of the header <complex>. Unless you are very
serious about writing highly portable code, you can probably omit such
tests.
construction You can construct an object of a floating-point class in several ways:
float_complex fO; II becomes (O.OF, O.OF)
float_complex fl(3.0F); II becomes (3.0F, O.OF)
float_complex f2(3.0, -2) II becomes (3.0F, -2.0F)
double_complex dO; II becomes (0.0, 0.0)
double_complex dl(3.0F); II becomes (3.0, 0.0)
double_complex d2(3.0, -2); II becomes (3.0, -2.0)
double_complex d3(fl); II becomes (3.0, 0.0)
long_double_complex IdO; II becomes (O.OL, O.OL)
long_double_complex Idl(3.0F); II becomes (3.0L, O.OL)
long_double_complex Id2(3.0, -2); II becomes (3.0L, -2.0L)
long_double_complex ld3(fl); II becomes (3.0L, O.OL)
long_double_complex Id3(d2); II becomes (3.0L, -2.0L)
516 Chapter 21
In other words, you can specify in a complex class constructor: no compo-
nents, just the real component, or both real and imaginary components of
a complex value. Any components you omit become zero. You can also
construct a complex class object from a complex class object of the same or
lower precision. (I don't Sl10W the same-precision case, since that is the
usual default copy constructor.) These single-argument constructors also
supply a number of useful implicit conversions, in a variety of contexts.
Thus, for example, you can often write a double value (real component) or
a float_complex value where a double_complex value is required.
_float_ You can also convert to a complex class of lower precision by explicitly
complex calling a conversion function. Using the objects declared in the examples
above, you can write any of:
_double_ fO _float_camplex(dl);
complex fO i= _float_camplex(ldl);
dO = _double_complex(ldl);
Naturall)" such conversions can result in floating-point overflow, under-
flow, or loss of precision, in either or both components of the converted
value. Use them cautiously.
notation All other functions are common to all three complex classes. For brevity
in describing them, I introduce the following common notation:
- TC is the complex class, such as float_complex.
- T is the corresponding floating-point type, such as float.
- xO and xl are objects of class Te.
- f is a value of type T.
I explain any other notation as needed.
arithmetic The following arithmetic operators are overloaded for each of the com-
operators plex classes. The operators have their usual arithmetic meaning, as ex-
tended into the complex plane:
xO += xl xO + xl xG + f f + xO
xO -= xl xO - xl xO - f f - xO
xO *= xl xO * xl xG * f f * xO
xO /= xl xO / xl xO / f f / xO
+xO xO == xl xO == f f == xO
-xO xO != xl xG != f f != xO
A number of functions are also overloaded for each of the complex
classes. One group returns a value of type T:
abs - abs (xO) , returns the magnitude of xO (the p polar component)
arg - arg (xO), returns the argument of xO (the e polar component)
imag - imag(xO), returns the imaginary component of xO (the y Cartesian
component)
norm - norm(xO), returns the squared magnitude of xO (x2 + y2)
real - real (xO), returns the imaginary component of xO (the x Cartesian
component)
Note that a class TC supplies no member functions for accessing the stored
components of the complex value. Use the above functions instead.
<complex> 517
Another group of functions returns a value of type TC:
conjg - conjg(xO), returns the conjugate of xO (the value x-i*y) in Cartesian
components)
cos - cos (xO) , returns the cosine of xO
cosh - cosh(xO), returns the hyperbolic cosine of xO
exp - exp (xO) , returns the exponential of xO
log - log (xO) , returns the natural logarithm of xO
polar - polar (rho, theta), returns the complex value corresponding to the
magnitude rho and argument theta, both of type F
pow - pow (xO, xl), returns xO raised to the power xl
pow - pow ( f , xl), returns f raised to the power xl
pow - pow (xO, f), returns xO raised to the power f
pow - pow (xO, i), returns xO raised to the power i, where i has type int
sin - sin (xO), returns the sine of xO
sinh - sinh(xO), returns the hyperbolic sine of xO
sqrt - sqrt (xO), returns the square root of xO
Finally, you can insert and extract objects of class c. For example:
extractor cin » xO
extracts a pair of Cartesian component values from the standard input
stream, constructs from them an object of class TC, and assigns it to xo. The
components are enclosed in parentheses and separated by a comma, as in
(2, -4.5).
And last of all, you can insert an object of class TC into, sa)', the standard
output stream, by writing:
inserter cout « xO
As you might expect, the components have a format acceptable to the
extractor, above, such as (2, -4.5). Format flags have their usual effect on
each of the floating-point components. (See the general discussion begin-
ning on page 124.) Be warned, however, that a nonzero width field affects
only the first of the two components. It is set to zero, as usual, after the first
floating-point component is inserted.
Implementing <complex>
complex Figure 21.1 shows the file complex, which implements the standard
header <complex>. It is very large, one of the largest files in this book.
Nevertheless, it is also one of the simpler headers. Most of its bulk results
from saying almost exactly the same thing for each of the three complex
classes float_complex, double_complex, and long_double_complex.
You should encounter just one or two surprises.
_Real One surprise is the addition to each class of the secret member functions
_Imag _Real and _Imag. These, of course, let you access the stored values of the
two Cartesian components. I chose to add these member functions rather
than make friend functions of real and imago
518 Chapter 21
Figure 21.1: II complex standard header
#ifndef _COMPLEX_
complex
#define _COMPLEX_
Part 1 of 5 #include <istream>
#include <ostream>
#define __STD_COMPLEX
II class float_complex
class float_complex {
public:
float_complex(float _R = 0, float _I = 0)
: _Re(_R), _Im(_1) {}
float_complex operator+=(float_complex _R)
{_Re += _Re_Real(), _1m += _Re_1mag();
return (*this); }
float_complex operator-=(float_complex _R)
{_Re -= _Re_Real(), _1m -= _Re_1mag();
return (*this); }
float_complex operator*=(float_complex);
float_complex operator/=(float_complex);
float _Real() const
{return (_Re);
float _1mag() const
{return (_1m);
private:
float _Re, _Im;
} ;
II class double_complex
class double_complex {
public:
double_complex(double _R = 0, double _I = 0)
: _Re(_R), _1m(_1) {}
double_complex operator+=(double_complex _R)
{_Re += _Re_Real(), _1m += _Re_1mag();
return (*this); }
double_complex operator-=(double_complex _R)
{_Re -= _Re_Real(), _1m -= _Re_1mag();
return (*this); }
double_complex operator*=(double_complex);
double_complex operator/=(double_complex);
double _Real() const
{return (_Re); }
double _Imag() const
{return (_Im); }
private:
double _Re, _1m;
};
II class lon9_double_complex
class long_double_complex {
public:
long_double_complex(long double _R = 0, long double I 0)
: _Re(_R), _Im(_I) {}
long_double_complex operator+=(long_double_complex _R)
{_Re += _Re_Real(), _1m += _Re_1mag();
return (*this); }
long_double_complex operator-=(long_double_complex _R)
<complex> 519
Conftn~ng {_Re -= _R __Real(), _1m -= _R __1mag{);
return (*this); }
complex
long_double_complex operator*=(long_double_complex);
~rt2~5
long_double_complex operator/=(long_double_complex);
long double _Real() const
{return (_Re);
long double _1mag() const
{return (_1m);
private:
long double _Re, _1m;
};
II type definitions
typedef float_complex _FC;
typedef double_complex _DC;
typedef long_double_complex _LDC;
II float_complex functions
inline float imag(_FC _X)
{return (_X __1mag(»;
inline float real(_FC _X)
{return (_X __Real(»; }
inline _FC _float_complex(const _DC& _X)
{return (_FC«float)_X __Real(), (float)_X __Imag(»);
inline _FC _float_complex(const _LDC& _X)
{return (_FC«float)_X __Real(), (float)_X __lmag(»);
inline _FC operator+(_FC _L, _FC _R)
{return (_L += _R);
inline _FC operator+(_FC _L, float _R)
{return (_L += _R); }
inline _FC operator+(float _L, _FC _R)
{return (_FC(_L) += _R); }
inline _FC operator-(_FC _L, _FC _R)
{return (_L -= _R);
inline _FC operator-(_FC _L, float _R)
{return (_L -= _R); }
inline _FC operator-(float _L, _FC _R)
{return (_FC(_L) -= _R); }
inline _FC operator*(_FC _L, _FC _R)
{return (_L *= _R);
inline _FC operator*(_FC _L, float _R)
{return (_L *= _R); }
inline _FC operator*(float _L, _FC _R)
{return (_FC(_L) *= _R); }
inline _FC operator/(_FC _L, _FC _R)
{return (_L 1= _R);
inline _FC operator/{_FC _L, float _R)
{return {_L 1= _R)i }
inline _FC operator/(float _L, _FC _R)
{return (_FC(_L) 1= _R); }
inline _FC operator+(_FC _L)
{return (_L); }
inline _FC operator-{_FC _L)
{return (_FC(-real(_L), -imag(_L»); }
inline _Bool operator=={_FC _L, _FC _R)
{return (real (_L) == real{_R) && imag(_L) imag(_R»;}
inline _Bool operator=={_FC _L, float _R)
520 Chapter 21
Continuing {return (real (_L) == _R && imag(_L) == 0);
complex inline _Bool operator==(float _L, _FC _R)
{return (_L == real(_R) && 0 == imag(_R»;
Part 3 of 5 inline _Bool operator!=(_FC _L, _FC _R)
{return (!(_L == _R»; }
inline _Bool operatorl=(_FC _L, float _R)
{return (!(_L == _R»; }
inline _Bool operator!=(float _L, _FC _R)
{return (!(_L == _R»; }
istream& operator»(istream&, _FC&);
ostream& operator«(ostream&, _FC);
float abs (_FC);
float arg(_FC);
inline _FC conj(_FC _X)
{return (_FC(real(_X), -image_X»~); }
_FC cos(_FC);
_FC cosh(_FC);
_FC exp(_FC);
FC log(_FC);
inline float norm(_FC _X)
{return (real (_X) * reale_X) + image_X) * image_X»~; }
_FC polar(float, float);
_Fe pow(_FC, _FC);
_FC pow(_FC, float);
_FC pow(_FC, int);
_FC pow(float, _FC);
_FC sin(_FC);
_FC sinh(_FC);
_FC sqrt(_FC);
II double_complex functions
inline double imag(_DC _X)
{return (_Xe_Imag(»;
inline double reale_DC _X)
{return <_Xe_Real(»; }
inline _DC _double_complex(const _LDC& _X)
{return <_DC«double)_X._Real(), (double)_Xe_lmag(»);
inline _DC operator+(_DC _L, _DC _R)
{return <_L += _R);
inline _DC operator+(_DC _L, double _R)
{return (_L += _R); }
inline _DC operator+(double _L, _DC _R)
{return <_DC (_L) += _R); }
inline _DC operator-(_DC _L, _DC _R)
{return <_L -= _R);
inline _DC operator-(_DC _L, double _R)
{return <_L -= _R); }
inline _DC operator-(double _L, _DC _R)
{return <_DC (_L) -= _R); }
inline _DC operator*(_DC _L, _DC _R)
{return <_L *= _R);
inline _DC operator*(_DC _L, double _R)
{return <_L *= _R);
inline _DC operator*(double _L, _DC _R)
{return <_DC (_L) *= _R)j }
inline _DC operator/(_DC _L, _DC _R)
<complex> 521
xldouhle Naturall)!, the same trick works for the third member of each triplet as
well. Figure 21.4 shows the file xldouble, which implements the internal
header <xldouble>. It defines the types needed to generate the long_dou-
hIe_complex version of each function definition (also not shown).
526 Chapter 21
The other business of the header <xcomp1ex> is to define a number of
internal functions used by many of the functions declared in <complex>.
These internal functions perform calculations that need delicate attention
to avoid spurious floating-point overflow, or to return exotic codes, or to
test for those exotic codes.
<xmath.h> The version of the file xcomp1ex I show here is designed to work with
an enhanced version of my implementation of the Standard C library
(Pla92). It includes yet another secret header from that older librar}', called
<xma.th.h>. That header defines the magic functions used by the inline
fun.etion definitions in xcomp1ex. To support the remaining source code in
this chapter, you have three choices:
- Use the enhanced Standard C libral)', which defines <xmath.h> as
required.
- Write your own version of the file xmath.h, as described more fully in
Appendix A: Interfaces.
- Write your own version of the file xcomplex, implementing the functions
as described below.
Whichever approach you take, you should know what functions the
header <xcomp1ex> must declare, either directly or indirectly: All are
overloaded for all three complex classes and/ or the corresponding float-
ing-pointtype. All also implicitly assume floating-point arithmetic compat-
ible with (or strongly resembling) the IEEE-754 Standard for floating-point
arithmetic (IEE85). For a discussion of the merits of that assumption, see
Pla92.
In the descriptions that follow, I call the complex class TC and the
corresponding floating-point type T. Note that the function _Fabs, in all
three precisions, is defined later in this chapter. It is the only function
declared in <xcomp1ex> that is supplied as a separate source file here:
_Cosh - T _Cosh (T x, T y) returns y times the hyperbolic cosine of x.
_Exp - T _Exp (T *px, T y, short e) returns y times the exponential of *px
times 2 raised to the e power.
_Fabs - T _Fabs (TC xo, int *pe) stores a scale factor in *pe and returns the
value z such that the magnitude of xO is z times 2 raised to the *pe power.
For any finite z, *pe is nonzero and even.
_Infv - T _Infv(T) returns the code for infinit}', or Inf. The argument value is
unused.
_Isinf - _Boo1 _Iainf (T x) returns a nonzero value if x is any code for infinity,
either +Inf or -Inf.
_Isnan - _Boo1 _Isnan (T x) returns a nonzero value if x is any code for
not-a-number, or NaN.
_Nanv • T _Nanv(T) returns some code fornot-a-number, or NaN. The argument
value is unused.
_Sinh • T _Sinh (T x, T y) returns y times the hyperbolic sine of x.
<complex> 527
Figure 21.5: II dcdiv -- TC::operator/=(TC)
#include <xcomplex>
dcdiv.c
TC TC::operator/=(TC rop)
( II divide TC by TC
T re = real(rop);
T im = imag(rop);
if (_Isnan(re) II _Isnan(im»
_Re = _Nanv(_Re), _Im =_Re;
else
( II rop has ordered components, divide
if (re < 0)
re = -reI
if (im < 0)
im = -im;
if (im < re)
( II limagl is smaller for rop
T r = imag(rop) I real(rop);
T d = real(rop} + r * imag(rop);
if (_Isnan(d) I I d == 0)
_Re = _Nanv(_Re}, _7m = _Re;
else
{ II denom is nonzero
T t (_Re + _Im * r) I d;
_Im (_Im - _Re * r) I d;
_Re t;
}
else if (im == O)
_Re = _Nanv(_Re), _Im = _Re;
else
( II Ireall is smaller for rop
T r = real(rop) I imag(rop};
T d = imag(rop} + r * real(rop);
if (_Isnan(d) I I d ==
0)
_Re =
_Nanv(_Re}, _Im = _Re;
else
{ II denom is finite
T t (_Re * r + _Im) I d;
Im (_Im * r - _Re) I d;
_Re t;
return (*this);
o
else
{ II 1 <= alb <= 2
static const T r2 1.4142135623730950488L;
static const T xh 2.4142L;
static const T xl 0.0000135623730950488016887L;
const T q t I b;
const T r (q + 2) * q;
const T s r I (r2 + sqrt(r + 2» + xl + q + xh;
return (a + b Is);
The algorithm is a bit tricky, and hard to describe. (See Kah87.) Put
simply, it works progressively harder the closer the two Cartesian compo-
nellts get in nlagnitude. In the extreme case, it indulges in a kind of
extended precision to paste the final answer together.
dcabs.c Figure 21.8 shows the file dcabs •c. It defines the function abs, the visible
interface to _Fabs. Any scaling occurs in the function ldexp, which pre-
sumably can deal with floating-point overflow or underflow gracefully.
dcarg.c Figure 21.9 shows the file dcarg.c. It defines the function arg, which
computes the argument, or phase angle. Fortunately, the function atan2
does exactly this job, given the Cartesian components. arg is not defined
inline in the header only because atan2 must not be made visible there.
dcpolar.c Figure 21.10 shows the file dcpolar.c. It defines the function polar in
the obviollS way. Again, it is almost a candidate for defining inline, except
for name visibility issues.
dccos.c Four functions are very similar. Figure 21.11 shows the file dccos. c,
dccosh.c which defines the function cos. Figure 21.12 shows the file dccosh.c,
dcsin.c wilich defines tIle function cosh. Figure 21.13 shows the file dcsin.c,
dcsinh.c whicil defines the function sin. And Figure 21.14 shows the file dcsinh.c,
wllich defines tIle function sinh. All form the components of the complex
result by calling _Cosh and _Sinh with various arguments. These functions
presumably avoid any intermediate overflow.
<complex> 531
dcexp.c Figure 21.15 shows the file dcexp. c, which defines the function expo It
faces a problem similar to that faced by the previous group of functions. It
also handles the problem in a similar wa)T, by calling _Exp to form the final
product carefully. In principle, the function need compute the exponential
only once. Doing so, however, sidesteps the safety provided by the function
-Exp.
dclog.c Figure 21.16 shows the file dclog. c, which defines the function log. It
essentially computes the logarithm by first converting to polar form:
In(p*exp(i*8)) = In(p) + i*8
The result ends up neatly in Cartesian form. The code takes some care to
handle and generate Inf and NaN codes. Beyond that, the only trickery lies
in the careful way the function assembles the real component. The sum of
c1 and c2 is In(2), with c1 contrived to have at most fifteen nonzero fraction
bits. This is again a simple form of extended precision.
dcsqrt.c Figure 21.17 shows the file dcsqrt. c, which defines the function sqrt.
Several methods exist for computing the complex square root. The one I
chose here is about as twisty as the logic in _Fabs, and for much the same
reasons. It endeavors to minimize computations, and preserve precision,
by hand crafting the simple solution for an argument value in each of the
four different quadrants.
dcpowcc.c The function pow(xO, xl) is overloaded for each complex class with
dcpowcf.c four combinations of parameter types. Three of these functions work
dcpowfc.c particularly closely together. Figure 21.18 shows the file dcpowcc •c, which
defines the function pow (TC, Te). Figure 21.19 shows the file dcpowcf .c,
which defines the function pow (TC, T). And Figure 21.20 shows the file
dcpowfc • c, which defines the function pow (T, TC). These filter out zero
components one at a time, in the hopes of achieving useful strength
reduction.
532 Chapter 21
Figure 21.13: II dcsin -- sin(TC)
#include <xcomplex>
dcsin.c
TC sin(TC x)
{ II find TC sin
return (TC(_Cosh(imag(x), sin(real(x»),
_Sinh(imag(x), cos(real(x»»);
o
o
<complex> 533
Figure 21.17: II dcsqrt -- sqrt(TC)
#include <xcomplex>
dcsqrt.c
TC sqrt(TC x)
{ II find square root of TC
int xexp;
T rho = _Fabs(x, &xexp)i
if (xexp == 0)
return (TC(rho, rho»;
else
{ II magnitude finite, compute square root
T remag = Idexp(real(x) < 0 ? - real(x) : real(x),
-xexp) ;
rho = ldexp(sqrt(2 * (remag + rho», xexp I 2 - 1);
if (0 <= real(x»
return (TC(rho, imag(x) I (2 * rho»);
else if (imag(x) < 0)
return (TC(-imag(x) I (2 * rho), -rho»;
else
return (TC(imag(x) I (2 * rho), rho»;
Testing <complex>
tcomplex.c Figure 21.24 SllOWS the file tcomplex.c. It tests the basic properties of
the three classes defined in <complex>. It d.oes so by repeating much the
same sequence of tests for class float_complex (in function tf (»), class
double_complex (in function td(», and long_double_complex (in func-
tion tld(».
Three versions of the function approx test the accuracy of computed
values. In all three precisions, approx permits an error in at most the two
least-significallt bits of the result. Tllat's not to sa)', however, that the tested
functions meet this criterion for all meaningful argument values. The tests
are merely anecdotal, and are far from complete by any reasonable metric
for test coverage. If all goes well, tIle program prints:
SUCCESS testing <complex>
and takes a normal exit.
Exercises
Exercise 21. 1 Determine what YOllr implementation does when a program:
• divides by zero
IIperforms arithmetic on an Inf or NaN code
• generates a floating-point overflow
• generates a floating-point underflow
Exercise 21.2 Alter the classes and functions defined in header <complex> to avoid all
arithmetic operations on Inf or NaN values. What does this do to perform-
ance?
Exercise 21.3 Are there ever circumstances, in floating-point arithmetic, wllere (0 + B*i)
does not have the same effect as B*i? If so, describe one or more cases. If
not, explain why not.
Exercise 21.4 [Harder] Define the class double_imaginary, which represents only the
imaginary component of a double_complex object. Extend the capabilities
defined for double_complex to include this new class, as well as all sensible
combinations of double_imaginary and double_complex operands.
Exercise 21.5 [Very hard] Rewrite the classes defined in header <complex> to represent
all complex values within the tllree classes in polar form, then alter the
functions to work with tllis llew representation.
536 Chapter 21
Figure 21.24: II test <complex>
#include <assert.h>
tcomplex.c
#include <float.h>
Part 1 of 5 #include <math.h>
#include <string.h>
#include <complex>
#include <iostream>
#include <strstream>
#ifndef __STD_COMPLEX
terror macro __STD_COMPLEX is not defined
#endif
II static data
static float feps;
static double eps;
static long double ldeps;
void tf()
{ II test float_complex properties
float_complex fcO, fc1(1), fc2(2, 2);
feps = FLT_EPSILON * 4.0;
assert(real(fcO) a && imag(fcO) 0);
assert(real(fc1) == 1 && imag(fc1) 0);
assert (real(fc2) == 2 && imag(fc2) 2);
fcO += fc2, assert(real(fcO) 2 && imag(fcO) == 2);
fcO fel, assert(real(fcO) 1 && imag(fcO) == 2);
fcO *= fc2, assert(real(fcO) -2 && imag(fcO) == 6);
fcO 1= fc2, assert(real(fcO) 1 && imag(fcO) == 2);
II test arithmetic
fcO = _float_complex(double_complex(2, 3»;
assert(real(fcO) == 2 && imag(fcO) == 3);
fcO = _float_complex(long_double_complex(-4, -5»;
assert(real(fcO) == -4 && imag(fcO) -5);
fcO = 2 + fc2 + 3, assert(real(fcO) 7 && imag(fcO) 2);
fcO = 2 - fc2 - 3;
assert(real(fcO) == -3 && imag(fcO) -2);
fcO = 2 * fc2 * 3;
assert(real(fcO) == 12 && imag(fcO) 12);
fcO = 8 I fc2 I 2;
<complex> 537
Continuing assert (real(fcO) == 1 && imag(feO) == -1);
fcO = +fcl + -fc2;
tcomplex.c
assert(real(fcO) == -1 && imag(fcO) == -2);
Part 2 of 5 assert(fc2 == fc2 && fcl 1 && 1 fel);
assert(fc1 != fc2 && fcl != 0 && 3 != fe1);
/1 test I/O
istrstream istr(1I (3, -1) (002, 2el) II);
ostrstream ostr;
istr » fcO, assert (real (fcO) == 3 && imag(fcO) -1);
ostr « fcO, istr » fcO;
assert (real (fcO) == 2 && imag(feO) 20);
ostr « fcO « ends;
assert(strcmp(ostr.str(), "(3,-1)(2,20)11) == 0);
/1 test math functions
static const float e = {2.7182818284590452353602875};
static const float ln2 = {0.6931471805599453094172321};
static const float piby4 = {0.7853981633974483096156608};
static const float rthalf = {0.7071067811865475244008444};
float el = rthalf * (e + 1 I e) / 2;
float sl = rthalf * (e - 1 / e) / 2;
assert (approx(abs (float_complex (5, -12», 13»;
assert (arg(fcl) == 0 && approx(arg(fc2), piby4»;
assert (conj (fc2) == float_complex(2, -2»;
fcO = cos(float_complex(piby4, -1»;
assert(approx(real(fcO), cl) && approx(imag(fcO), sl»;
fcO = eosh(float_complex(-l, piby4»;
assert(approx(real(fcO), cl) && approx(imag(fcO), -sl»;
fcO = exp(fcl);
assert(approx(real(feO), e) && imag(fcO) == 0);
fcO = exp(float_complex(l, -piby4»;
assert(approx(real(fcO), e * rthalf)
&& approx(imag(fcO), -e * rthalf»;
fcO = log (float_complex(l, -1»;
assert(approx(real(fcO), ln2 I 2)
&& approx(imag(fcO), -piby4»;
assert(no~(float_complex(3, -4» 25 && no~(fe2) 8);
fcO = polar(l, -piby4);
assert(approx(real(feO), rthalf)
&& approx(imag(fcO), -rthalf»;
fcO = pow(fc2, fc2);
fcO = pow(fc2, 5);
assert (real (fcO) == -128 && imag(fcO) == -128);
fcO = pow(fc2, (float)2);
assert(approx(real(fcO), 0) && approx(imag(fcO), 8»;
fcO = pow«float)2, fe2);
fcO = sin(float_complex(piby4, -1»;
assert(approx(real(fcO), cl) && approx(imag(fcO), -sl»;
fcO = sinh(float_eomplex(-l, piby4»;
assert(approx(real(fcO), -sl) && approx(imag(fcO), el»;
fcO = sqrt(float_complex(O, -1»;
assert(approx(real(fcO), rthalf)
&& approx(imag(fcO), -rthalf»;
void td()
538 Chapter 21
Continuing { II test double_complex properties
double_complex fcO, £cl(l), fc2(2, 2);
tcomplex.c
eps = DBL_EPSILON * 4.0;
Part 3 of 5 assert (real(feO) 0 && imag(fcO) 0);
assert(real(fel) == 1 && imag(fcl) 0);
assert(real(fe2) == 2 && imag(fc2) 2);
fcO += fe2, assert(real(fcO) 2 && imag(fcO) == 2);
fcO fel, assert(real(fcO) 1 && imag(fcO) == 2);
fcO *= fe2, assert(real(fcO) -2 && imag(fcO) == 6);
fcO 1= fe2, assert(real(fcO) 1 && imag(fcO) == 2);
II test arithmetic
fcO = _double_complex(long_double_complex(-4, -5»;
assert (real(feO) == -4 && imag(fcO) -5);
fcO = 2 + fc2 + 3, assert(real(fcO) 7 && imag(fcO) 2);
fcO = 2 - fc2 - 3;
assert(real(feO) == -3 && imag(fcO) -2);
fcO = 2 * fc2 * 3;
assert(real(feO) == 12 && imag(fcO) 12);
fcO = 8 I fc2 I 2;
assert(real(feO) == 1 && imag(fcO) == -1);
fcO = +fel + -fc2;
assert(real(feO) == -1 && imag(fcO) == -2);
assert(fe2 == fc2 && fcl 1 && 1 fcl);
assert(fel != fc2 && fcl 1= 0 && 3 != fcl);
II test IIO
istrstream istr(II(3, -1) (002, 2el)II);
ostrstream ostr;
istr » fcO, assert(real(fcO) 3 && imag(fcO) -1);
ostr « fcO;
istr » fcO, assert (real (fcO) 2 && imag(fcO) 20);
ostr « fcO « ends;
assert (strcmp(ostr. str (), II (3, -1) (2,20) .. ) == 0);
II test math functions
static const double e = {2.7182818284590452353602875};
static const double ln2 = {0.6931471805599453094172321};
static const double piby4 = {0.7853981633974483096156608};
static const double rthalf = {0.7071067811865475244008444};
double cl = rthalf * (e + 1 I e) I 2;
double sl = rthalf * (e - 1 I e) I 2;
assert (approx(abs (double_complex(5, -12», 13»;
assert (arg(fcl) == 0 && approx(arg(fc2), piby4»;
assert (conj (fc2) == double_complex(2, -2»;
fcO = cos(double_complex(piby4, -1»;
assert(approx(real(fcO), cl) && approx(imag(fcO), sl»;
fcO = cosh(double_complex(-l, piby4»;
assert(approx(real(fcO), cl) && approx(imag(fcO), -sl»;
fcO = exp(fcl);
assert(approx(real(fcO), e) && imag(fcO) == 0);
fcO = exp(double_complex(l, -piby4»;
assert(approx(real(fcO), e * rthalf)
&& approx(imag(fcO), -e * rthalf»;
fcO = log (double_complex(l, -1»;
assert(approx(real(fcO), In2 I 2)
&& approx(imag(fcO), -piby4»;
assert (norm (double_complex (3, -4» == 25 && no~(fc2) 8);
<complex> 539
void tld{ )
{ // test long_double_complex properties
long_double_complex fcO, fcl{l), fc2{2, 2);
ldeps = LDBL_EPSILON * 4.0;
assert (real (fcO) 0 &&: imag (fcO) 0) ;
assert {real (fcl) == 1 &&: imag{fcl) 0);
assert {real (fc2) == 2 &&: imag{fc2) 2);
fcO += fc2, assert {real (fcO) 2 && imag{fcO) == 2);
fcO fcl, assert {real (fcO) 1 && imag{fcO) == 2);
fcO *= fc2, assert{real(fcO) -2 && imag{fcO) == 6);
fcO /= fc2, assert{real(fcO) 1 && imag{fcO) == 2);
// test arithmetic
fcO = 2 + fc2 + 3, assert {real (fcO) 7 && imag{fcO) 2);
fcO = 2 - fc2 - 3;
assert (real{fcO) == -3 &:& imag{fcO) -2);
fcO = 2 * fc2 * 3;
assert {real (fcO) == 12 &:& imag{fcO) 12);
fcO = 8 / fc2 / 2;
assert (real{fcO) == 1 &&: imag{fcO) == -1);
fcO = +fcl + -fc2;
assert {real (fcO) == -1 &:& imag{fcO) == -2);
assert{fc2 == fc2 && fcl 1 && 1 fcl);
assert(fcl != fc2 && fcl != 0 && 3 != fcl);
// test I/O
istrstream istr( II (3, -1) (002, 2el) II);
ostrstream ostr;
istr » fcO, assert (real{fcO) 3 && imag{fcO) -1);
ostr « fcO;
istr » fcO, assert {real (fcO) 2 && imag{fcO) 20);
ostr « fcO « ends;
assert{strcmp(ostr.str(), II (3,-1) (2,20) n) == 0);
// test math functions
static const long double e =
{2.7182818284590452353602875L}i
static const long double In2 =
{0.6931471805599453094172321L};
static const long double piby4 =
540 Chapter 21
Continuing {0.7853981633974483096156608L};
static const long double rthalf =
tcomplex.c
{0.7071067811865475244008444L};
Part 5 of 5 long double cl = rthalf * (e + 1 I e) I 2;
long double sl = rthalf * (e - 1 I e) I 2;
assert (approx(abs (long_double_complex(5, -12», 13»;
assert (arg(fcl) == 0 && approx(arg(fc2), piby4»;
assert (conj (fc2) == long_double_complex(2, -2»;
fcO = cos(long_double_complex(piby4, -1»;
assert(approx(real(fcO), cl) && approx(imag(fcO), sl»;
fcO = cosh(long_double_complex(-l, piby4»;
assert(approx(real(fcO), cl) && approx(imag(fcO), -sl»;
fcO = exp(fcl);
assert(approx(real(fcO), e) && imag(fcO) == 0);
fcO = exp(long_double_complex(l, -piby4»;
assert(approx(real(fcO), e * rthalf)
&& approx(imag(fcO), -e * rthalf»;
fcO = log(long_double_complex(l, -1»;
assert(approx(real(fcO), ln2 I 2)
&& approx(imag(fcO), -piby4»;
assert (norm(long_double_complex(3, -4» 25
&& norm(fc2) == 8);
fcO = polar(l, -piby4);
assert(approx(real(fcO), rthalf)
&& approx(imag(fcO), -rthalf»;
fcO = pow(fc2, fc2);
fcO = pow(fc2, 5);
assert (real (fcO) == -128 && imag(fcO) == -128);
fcO = pow(fc2, (long double)2);
assert(approx(real(fcO), 0) && approx(imag(fcO), 8»;
fcO = pow«long double)2, fc2);
fcO = sin(long_double_complex(piby4, -1»;
assert(approx(real(fcO), cl) && approx(imag(fcO), -sl»;
fcO = sinh(long_double_complex(-l, piby4»;
assert(approx(real(fcO), -sl) && approx(imag(fcO), cl»;
fcO = sqrt(long_double_complex(O, -1»;
assert(approx(real(fcO), rthalf)
&& approx(imag(fcO), -rthalf»;
int main ( )
( II test basic workings of complex definitions
tf();
td() ;
tld() ;
cout « "SUCCESS testing <complex>" « endl;
return (0);
D
Appendix A: Interfaces
• The Standard C++ library has to know how to cooperate with the
Standard C library in such matters as defining the same type in multiple
headers. Non-standard method must invariably be employed.
• Extractors in the Standard C++ library need to make floating-point
conversions not adequately supported by the Standard C library Extra
functions must be supplied.
• Buffering of file reads and ",rrites requires both close coordination be-
tween the two libraries and the greatest possible optimization for each.
Knowledge of internal structures must be shared to achieve both goals.
• C++ exists in multiple dialects, thanks to the addition of major language
features over several years of standardization. Macros and type defini-
tions must be defined to gloss over these differences.
• The C Standard itself is changing with the acceptance of Amendment 1
(15094). For full compliance, extra functionality must be supplied.
For all these reasons, a typical implementation of the Standard C library
needs supplementing. And a typical implementation of C++ needs to
provide a few hints about what it can and cannot do.
<yxvals •h> I've concentrated all the hints - in the form of implementation-depend-
ent macros and type definitions - in a single "internal" header called
<yxvals .h>. (Yes, the name is weird. It is modeled after the analogous file
<yvals. h> in the PJP C library) Figure A.I shows one way to write the file
yxvals.h, to implement this header. This particular version represents a
full implementation of the language described by the draft C++ Standard.
It also assumes that the underlying Standard C library is from Pla92.
542 Appendix A
Figure A.l: II yxva1s.h sample values header for c++
#ifndef _YXVALS_
yxvals.h
#define _YXVALS_
II translator/library features
#define _CATCH_IO_EXCEPTIONS /* try blocks in iostreams *1
#define _HAS_ARRAY_NEW 1* operator new[] implemented *1
#define _HAS_PJP_CLIB /* running atop PJP C Library *1
#define _HAS_SIGNED_CHAR 1* char/signed char distinct *1
#define _HAS_TYPEINFO 1* typeid implemented *1
II bitmask macros enum overloading implemented
#define _BITMASK(E, T) \
E& operator&=(E& _X, E _Y) \
{_X = (E) (_X & _Y); return (_X) ; \
E& operatorl=(E& _X, E _Y) \
{_X = (E) (_X I _Y); return (_X) ; \
E& operatorA=(E& _X, E _Y) \
{_X = (E) (_X A _Y); return (_X); \
E& operator&(E _X, E _Y) \
{return «E) (_X & _Y»; \
E& operatorl(E _X, E _Y) \
{return «E) (_X I _Y»; \
E& operatorA(E _X, E _Y) \
{return «E) (_X A _Y»; \
E& operator-(E _X) \
{return «E)-_X); } \
typedef E T
II exception macros -- exceptions implemented
#define _TRY_BEGIN try {
#define _CATCH_ALL } catch ( ••• )
#define _CATCH_END }
#define _RAISE (x) throw (x)
#define _RERAISE throw
II numeric representation macros
#define _BITS_BYTE 8
#define _MAX_EXP_DIG 8
#define _MAX_INT_DIG 32
#define _MAX_SIG_DIG 36
II type definitions
typedef boo1 _Boo1;
struct _Filet;
struct _Fpost;
/* #define _PTRDIFFT /* only if ptrdiff_t already defined */
typedef int _Ptrdifft
1* #define _SIZET 1* only if size_t already defined */
typedef unsigned int _Sizet;
typedef int _Typedesc;
#define _WCHART 1* only if wchar_t already defined *1
typedef unsigned short _Wchart;
1* #define _WINTT 1* only if wint_t already defined *1
typedef unsigned short _Wintt;
#endif 1* _YXVALS_ *1 o
Interfaces 543
Here is a summary of all tl1e types a11d ll1acros defined il1 <yxvals.h>.
It suggests changes to make if these assumptions do not alll10ld:
_BITS_BYTE. _BITS_BYTE - defined as a macro that expands to an integer constant
expression. Its value is tl1e number of bits i11 a byte (at least 8).
_Bool - a synonym for the newly added type bool. For an implementa-
tion tl1at does not support the keyword bool, define this type as into
_CATCH_ALL - the macro that introduces an all-purpose catch clause,
after _TRY_BEGIN and before _CATCH_END. For an implementation that
does not support exceptions, you can define tl1is macro as} if (0) {.
_CATCH_END - the ll1acro that tenninates a11 all-purpose catch clause,
after _TRY_BEGIN and _CATCH_END. For an implementation that does not
support exceptions, you can define this macro as }}.
_CATCH_IO_ • _CATCH_IO_EXCEPTIONS - defined as a macro if you want each inserter
EXCEPTIONS and extractor to be wrapped in a tn) block, as tl1e draft C++ Standard
requires. Omit the macro definition if you don't want all this code.
_Filet • _Filet - a synonym for tl1e type FILE, declared in <stdio .h>. The PJP
C library actually defines FILE as struct _Filet. For another Standard
C libral)T, you may have to add a type definition.
_Fpost • _!'post - a synonym for the type fpos_t, defined i11 <stdio.h>. Tl1e
PJP C library actually defh1es fpos_t as struct _Fpost. For another
Standard C librar)', you may have to add a type defi11ition.
_HAS_ • _HAS_ARRAY_NEW - defined as a macro if the implementation accepts
ARRAY_NEW the function names operator delete [] and operator new [] . (See the
code for delaop. c on page 92 and for newaop • c on page 92.) Omit the
macro definitio11 if such function nalnes are not accepted.
_HAS_ • _HAS_PJP_CLIB - defined as a macro if you want the filebuf virtual
PJP_CLIB member functions to directly manipulate FILE objects as defined in the
PJP C library. (See the code for f ilebuf c beginning on page 330.) Omit
0
FLT_RAD:IX <float.h>
FLT_ROUNDS <float.h>
FOPEN_MAX <stdio.h>
HUGE_VAL HUGE_VAL <math.h>
:INT_MAX <limits.h>
:INT_M:IN <limits.h>
LC_ALL <locale.h>
LC_COLLATE <locale.h>
LC_CTYPE <locale.h>
LC_MONETARY <locale.h>
LC_NUMER:IC <locale.h>
LC_TlME <locale.h>
LDBL_D:IG <float.h>
LDBL_EPS:ILON <float.h>
LDBL_MANT_D:IG <float.h>
LDBL_MAX <float.h>
LDBL_MAX_l0 - EXP <float.h>
LDBL_MAX_EXP <float.h>
LDBL_M:IN <float.h>
LDBL_M:IN_l0_EXP <float.h>
LDBL_M:IN_EXP <float.h>
LONG_MAX <limits.h>
LONG_M:IN <limits.h>
L_tmpnam <stdio.h>
MB_CUR_MAX <stdlib.h>
MB_LEN_MAX <limits.h>
NPOS NPOS <defines> defines 46
NULL <locale.h>
NULL <stddef.h>
NULL <stdio.h>
NULL <stdlib.h>
NULL <string.h>
NULL <time.h>
NULL <wchar.h>
RAND_MAX <stdlib.h>
SCHAR_MAX <limits.h>
SCHAR_M:IN <limits.h>
SEEK_CUR <stdio.h>
SEEK_END <stdio.h>
SEEK_SET <stdio.h>
SHRT_MAX <limits.h>
SHRT_M:IN <limits.h>
S:IGABRT <signal.h>
S:IGFPE <si.gnal.h>
S:IG:ILL <signal.h>
S:IG:INT <signal.h>
S:IGSEGV <signal.h>
S:IGTERM <signal.h>
S:IG_DFL <signal.h>
S:IG_ERR <signal.h>
S:IG_:IGN <signal.h>
TMP_MAX <stdio.h>
UCHAR_MAX <limits.h>
U:INT_MAX <limits.h>
ULONG_MAX <limits.h>
USHRT_MAX <limits.h>
WCHAR_MAX <wchar.h>
WCHAR_M:IN <wchar.h>
WEOF <wchar.h>
WEOF <wctype.h>
- :IOFBF <stdio.h>
:IOLBF <stdio.h>
:IONBF <stdio.h>
_double_complex <complex> complex 518
_float_complex <complex> complex 518
abort abort <stdlib.h>
abs <complex> dcabs.c 530
Names 549
NAME HEADER FILE PAGE
fputc <stdio.h>
fputs <stdio.h>
fputwc <wchar.h>
fputws <wchar.h>
fread <stdio.h>
free <stdlib.h>
freopen <stdio.h>
frexp <math.h>
fscanf <stdio.h>
fseek <stdio.h>
fsetpos <stdio.h>
ftell <stdio.h>
fvoid -. t <defines> defines 46
fwide <wchar.h>
fwprintf <wchar.h>
fwrite <stdio.h>
fwscanf <wchar.h>
getc getc <stdio.h>
getchar <stdio.h>
getenv <stdlib.h>
getline <string> strgline.c 379
gets <stdio.h>
getwc <wchar.h>
getwchar <wchar.h>
gmtime <time.h>
hex <ios> hex.c 140
ifstream <fstream> fstream 326
ifstream::close <fstream> fstream 326
ifstream::ifstream <fstream> fstream 326
ifstream: : is_open <fstream> fstream 326
ifstream::open <fstream> fstream 326
ifstream::rdbuf <fstream> fstream 326
ifstream::-ifstream <fstream> ifstream.c 333
imag <complex> complex 518
imag <complex> complex 518
imanip<T> <iomanip> iomanip 258
imanip<T>::imanip <iomanip> iomanip 258
internal <ios> internal.c 140
invalidargument <exception> exceptio 68
invalidargument::do_raise <exception> invalida.c 74
invalidargument::invalidargument <exception> invalida.c 74
invalidargument::-invalidargument <exception> invalida.c 74
ios ios <ios> ios 129
ios:::Init <ios> ios 129
ios:::Init:::Init <ios> iostream.c 343
ios:::Init::-:Init <ios> iostream.c 343
ios::adjustfield <ios> ios 129
ios: :app <ios> ios 129
ios: :ate <ios> ios 129
ios: :bad <ios> ios 129
ios: : badbi t <ios> ios 129
ios::basefield <ios> ios 129
ios: :beg <ios> ios 129
ios: : binary <ios> ios 129
ios::clear <ios> ios 129
ios::clear <ios> ios.c 134
ios::copyfmt <ios> ios 129
ios::cur <ios> ios 129
ios: :dec <ios> ios 129
ios: : end <ios> ios 129
ios: :eof <ios> ios 129
ios::eofbit <ios> ios 129
ios::exceptions <ios> ios 129
ios::exceptions <ios> ios 129
ios::exceptions <ios> iosexcep.c 135
ios::fail <ios> ios 129
ios::failbit <ios> ios 129
Names 553
NAME HEADER FILE PAGE
ldiv <stdlib.h>
ldiv_t <stdlib.h>
left <ios> left.c 140
lengtherror <exception> exceptio 68
lengtherror::do_raise <exception> lengther.c 75
lengtherror::lengtherror <exception> lengther.c 75
lengtherror::-lengtherror <exception> lengther.c 75
localeconv <locale.h>
localtime <time.h>
log <complex> dclog.c 532
log <complex> fclog.c
log <complex> ldclog.c
log <math.h>
logf <math.h>
logl <math.h>
log10 <math.h>
long_double_complex <complex> complex 518
long_double_complex::
long_double_complex <complex> complex 518
long_double_complex::operator*= <complex> ldcmul.c
long_double_complex::operator+= <complex> complex 518
long_double_complex::operator-= <complex> complex 518
long_double_complex::operator/= <complex> ldcdiv.c
longjmp <setjmp.h>
malloc malloc <stdlib.h>
mblen <stdlib.h>
mbrlen <wchar.h>
mbrtowc <wchar.h>
mbsinit <wchar.h>
mbsrtowcs <wchar.h>
mbstowcs <stdlib.h>
mbtowc <stdlib.h>
memchr <string.h>
memcmp <string.h>
memcpy <string.h>
memmove <string.h>
memset <string.h>
mktime <time.h>
modf <math.h>
new <new> new 90
new <new> newop.c 91
new[] <new> new 90
new[] <new> newaop.c 92
norm <complex> complex 518
noshowbase <ios> nshowbas.c 141
noshowpoint <ios> nshowpoi.c 141
noshowpos <ios> nshowpos.c 141
noskipws <ios> noskipws.c 140
not keyword
not <iso646.h>
not _eq keyword
not_eq <iso646.h>
nouppercase <ios> noupperc.c 141
oct oct <ios> oct.c 141
offsetof <stddef.h>
of stream <fstream> fstream 326
ofstream::close <fstream> fstream 326
ofstream::is_open <fstream> fstream 326
ofstream::ofstream <fstream> fstream 326
ofstream::open <fstream> fstream 326
ofstream::rdbuf <fstream> fstream 326
ofstream::-ofstream <fstream> ofstream.c 333
omanip<T> <iomanip> iomanip 258
omanip<T>::omanip <iomanip> iomanip 258
operator! = <complex> complex 518
operator! = <string> string 363
operator! = <wstring> wstring 400
556 Appendix B
NAME HEADER FILE PAGE
- SIZET <string.h>
- SIZET <time.h>
- SIZET <wchar.h>
- SIZET <yxvals.h> yxvals.h 542
- SSTREAM_ <sstream> sstream 302
- STREAMBUF- <streambuf> streambu 163
- STRING_ <string> string 363
- STRSTREAM_ <strstream> strstrea 280
- Sizet <yxvals.h> yxvals.h 542
- Stod <yxvals.h> yxvals.h 204
- Stof <yxvals.h> yxvals.h 205
- Stold <yxvals.h> yxvals.h 206
_TRY_BEGIN _TRY_BEGIN <yxvals.h> yxvals.h 542
_TRY_IO_BEGIN <ios> ios 129
TYPEINFO_ <type info> type info 100
_Typedesc <yxvals.h> yxvals.h 542
_WCHART <defines> defines 46
_WCHART <stddef.h>
_WCHART <stdlib.h>
_WCHART <yxvals.h> yxvals.h 542
_Wchart <yxvals.h> yxvals.h 542
_WINTT <defines> defines 46
_WINTT <wchar.h>
_WINTT <wctype.h>
_WINTT <yxvals.h> yxvals.h 542
_Wintt <yxvals.h> yxvals.h 542
_WSTRING_ <wstring> wstring 400
_XCOMPLEX_ <xcomplex> xcomplex 524
bits<N> bits<N>:: _A <bits> bits 431
bits<N>:: _N <bits> bits 431
bits<N>:: _Nb <bits> bits 431
bits<N>:: _Nw <bits> bits 431
bits<N>:: T <bits> bits 431
bits<N>: :_Tidy <bits> bits 431
bits<N>:: _Trim <bits> bits 431
bits<N>: : _Xinv <bits> bits 431
bits<N>:: _Xoflo <bits> bits 431
bits<N>:: _Xran <bits> bits 431
bitstring::_Copylr <bitstring> bitscplr.c 459
bitstring::_Copyrl <bitstring> bitscprl.c 460
bitstring::_Grow <bitstring> bitstrin.c 454
bitstring::_Len <bitstring> bitstrin 452
bitstring::_Nb <bitstring> bitstrin 452
bitstring::_Ones <bitstring> bitstrin 452
bitstring: :_ptr <bitstring> bitstrin 452
bitstring::_Res <bitstring> bitstrin 452
bitstring::_Setl <bitstring> bitstrin 452
bitstring::_Source <bitstring> bitstrin 452
bitstring::_Src <bitstring> bitstrin 452
bitstring::_T <bitstring> bitstrin 452
bitstring::_Tidy <bitstring> bitstrin.c 454
bitstring::_W <bitstring> bitstrin 452
bitstring::_X <bitstring> bitstrin 452
bitstring::_Xinv <bitstring> bitstrin 452
bitstring::_Xlen <bitstring> bitstrin 452
bitstring::_Xran <bitstring> bitstrin 452
bitstring::_Zeros <bitstring> bitstrin 452
double_complex::_Im <complex> complex 518
double_complex::_lmag <complex> complex 518
double_complex::_Re <complex> complex 518
double_complex::_Real <complex> complex 518
dynarray<T> dynarray<T>: _Grow <dynarray> dynarray 484
dynarray<T>: - Len <dynarray> dynarray 484
dynarray<T>: - ptr <dynarray> dynarray 484
dynarray<T> - Res <dynarray> dynarray 484
dynarray<T> T <dynarray> dynarray 484
dynarray<T> _Tidy <dynarray> dynarray 484
Names 565
NAME HEADER FILE PAGE
This appendix lists terms that have special meaning within this book.
Check here if you suspect that a term means more (or less) than you might
ordinarily think.
The literature on C++ is vast, but not always useful. What follows is an
assortment of books and documents that are either directly related to the
material in this book or useful in their own right as tutorial material. This
list is not intended as an exhaustive list of books on the subject.
ANS89 ANSI Standard X3.159-1989 (New York NY: American National Standards
Institute, 1989). The original C Standard, developed by the ANSI-author-
ized committee X3J11. The Rationale that accompanies the C Standard
explains many of the decisions that went into it, if you can get your hands
on it.
C&W80 William J. Cody; Jr. and William Waite, Software Manual for the Elementary
Functions (Englewood Cliffs NJ: Prentice Hall, 1980). The definitive work
on writing and testing math functions.
E&590 Margaret A. Ellis and Bjame Stroustrup, The Annotated c++ Reference Man-
ual (Reading MA: Addison Wesley, 1990). Known popularly as the ARM.
This is the base document for the language portion of the draft C++ Standard.
You will find that the draft C++ Standard has already evolved well beyond
the ARM, at least in places.
IEE85 ANSI/IEEE Standard 754-1985 (Piscataway; N.J.: Institute of Electrical and
Electronics Engineers, Inc., 1985). The floating-point standard widely used
in modern microprocessors.
15090 ISO/IEC Standard 9899:1990 (Geneva: International Standards Organiza-
tion, 1990). The official C Standard around the world. Aside from format-
ting details and section numbering, the ISO C Standard is identical to the
ANSI C Standard.
15094 ISO/IEC Amendment 1 to Standard 9899:1990 (Geneva: International Stand-
ards Organization, 1994). The first (and only) amendment to the C Stand-
ard. It provides substantial support for manipulating large character sets.
K&589 Andrew Koenig and Bjarne Stroustrup, "C++: As Close to C as Possible-
But No Closer," The C++ Report, July 1989. An article from the early days of
the C++ standardization effort. It spells out ground rules for allowing
Standard C++ to differ from from Standard C in sensible ways.
Kah87 W. Kahan, "Branch Cuts for Complex Elementary Functions or Much Ado
About Nothing's Sign Bit," Proceedings of the joint IMA/SIAM Conference on
The State of the Art in Numerical Analysis, University of Birmingham, April
580 Appendix D
1986, edited by A. Iserles and M.J.D. Powell (Oxford: Clarendon Press,
1987). Describes some of the peculiarities of computing complex functions,
particularly using of IEEE 754 floating-point arithmetic.
Koe94 Andrew Koenig, editor, Working Paper for Draft Proposed International Stand-
ard for Information Systems - Programming Language C++ (committee work-
ing paper: WG21/N0414, X3J16/94-0027, 25 January 1994). The draft C++
Standard distributed within SC22, herein referred to as the Informal Re-
view Draft. It is subject to change by Committee vote three times per year
until approved.
Mey92 Scott Meyers, Effective c++ (Reading MA: Addison Wesle)T, 1992. Provides
"50 specific ways to improve your programs and designs," many of which
are gems.
Pla92 ~J. Plauger, The Standard C Library (Englewood Cliffs NJ: Prentice Hall,
1992) Contains a complete implementation of the Standard C libraI}', as well
a text from the library portion of the C Standard and guidance in using the
Standard C library. It is the predecessor and companion volume to this
book.
P&B92 ~J. Plauger and Jim Brodie, ANSIand ISO Standard C: Programmer's Reference
(Redmond WA: Microsoft Press, 1992). Provides a complete but succinct
reference to the entire C Standard. It covers both the language and the
librar)T.
Plu89 Thomas IJlum, C Programming Guidelines (CardiffNJ: Plum Hall, Inc., 1989).
An excellent style guide for writing C programs. It also contains a good
discussion of first-order correctness testing, on pp. 194-199.
P&S91 Thomas Plum and Dan Saks, C++ Programming Guidelines (Cardiff NJ: Plum
Hall, Inc., 1991). Another excellent style guide, this time for writing C++
programs.
S&L94 A.A. Stepanov and M. Lee, "The Standard Template LibraI}'," Technical
Report HPL-94-34 (Hewlett-Packard Laboratories, April 1994). The basis for
the STL addition to the draft Standard C++ Library.
Str91 Bjame Stroustrup, The C++ Programming Language, Second Edition (Reading
MA: Addison Wesley, 1991). An excellent overview by the developer of the
C++ language.
Str94 Bjame Stroustrup, The Design and Evolution of c++ (Reading MA: Addison
Wesle)T, 1994). More recent reflections on changes made to C++, including
those made as part of the standardization process.
Tea93 Steve Teale, C++ IOStreams Handbook (Reading MA: Addison-Wesle)T, 1993).
Describes many of the inner workings of the iostreams portion of a typical
c++ librar)T. The version covered predates the one chosen as the basis for
standardization, llowever.
Vil93 Mike Vilot, C++ Programming PowerPack (Carmel IN: Sams Publishing,
1993). An excellent example of the use of templates in building a powerful
library atop the Standard c++ library
Index
This index makes no attempt to provide all references to names defined in the code. For
a complete list of such names, and associated source files, see Appendix B: Names. For a list
of specialized terms used in this book, see Appendix C: Terms.
bits<N> 421-437,448
A bits<N> : : any 426, 429
abs 505,509,513,516,523,526 bits<N>: :bits 424,428
adjustfield 109, 126,223, 231-234 bits<N>: : count 426,429
alias 7, 36, 325, 430 bits<N>: : length 426,428
<all> 6-7, 14, 18,22 bits<N>: : operator 1 = 426,429
Amendment 1 xiii-xi~ 2, 6, 16, 36-38, 44, bits<N>: :operator&= 424,429
158,340,383,541,546,567,579 bits<N> : : operator== 426,429
ANSI xi-xii, 5-6, 567-572, 576-580 bits<N>: : operator A = 424,429
app 109,311,321-322 bits<N>: : operator 1= 424,429
arg 505,509,514,516 bit s <N> : : operator- 425, 429
<assert .h> 6-7, 14, 16,23,80 bits<N>: : operator» 426,429
assignment operator 10, 67, 72, 98-100, 133, bits<N>: :operator»= 425,429
473,475,487,567 bits<N>: :operator« 426,430
ate 109, 311, 322 bits<N>: :operator«= 424,429
atexit 31 bits<N>: : reset 425,429
bits<N>: : set 425,429
B bits<N>: : test 426,429
bits<N>: : to_string 426,429
bad_alloc 16, 63, 65, 82, 88 bits<N> : : to_ulong 425,429
bad_cast 63 bits<N>: :to_ushort 425,429
bad_typeid 63 bits<N>: : toggle 425,429
badbit 109,123,232 <bitstring> 6, 16, 35, 43, 46, 65, 439-471,
badcast::-badcast 59 473
badcast: :badcast 59 bitstring: : any 446,451
badtypeid::-badtypeid 97 bitstring: : append 443,449
badtypeid::badtypeid 97 bitstring: : assign 443,449
badtypeid::do_raise 97 bitstring: :bitstring 441-442,
basefield 109, 126, 189, 233 448-449
basic_string 357,394,415 bitstring: : count 445,451
beg 109, 160 bitstring: : find 446,450
Bell Laboratories 5, 578 bitstring: : insert 443,449
benign redefinition 24, 47, 568 bitstring: : length 445,448
bidirectional stream 107, 122, 188, 230,277 bitstring: :none 446,451
binary 109,311,321-322 bitstring: : operator 1= 446,450
binary file 113,321-322,325,573 bitstring: :operator&= 442,451
bit_string 448 bitstring: : operator+= 442, 449
bitmask type 11, 111-113, 121, 128, 132,268, bitstring::operator= 449
283,421 bitstring: :operator== 446,450
<bits> 6, 16, 65, 249, 251,421-438,458,474 bitstring: : operator A = 442,451
_BITS_BYTE 430,455,543
582
bitstring: : operator I = 442/451 <ctype"h> 6/14-16/126/182-183/193/357,
bitstring: : operator- 447/ 451 383
bitstring: : operator» 447/451 cur 109/ 160
bitstring: :operator»= 443/451
bitstring: :operator« 446/451 D
bitstring: :operator«= 443/451
bitstring: : remove 444/ 450 dec 109/117/119/124/126/138/187/229
bitstring: : replace 444/ 449 decimal-point character 12/ 111-112/ 126/
bitstring: : reset 444-445/451 193/206/208,234-235/569/577
bitstring: : resize 445/450 default argument 13/ 20/ 34/ 39/ 328/ 358/
bitstring: :rfind 446/450 395/483
bitstring: : set 444/451 default behavior 9/ 53/ 55/ 57/ 63/ 66-67/
bitstring: :substr 446/450 86-87/ 156-159/ 166/ 272, 297
bitstring: : test 446/ 451 default constructor 45/ 158/ 276/ 300/
bitstring: : to_string 445/451 321-322/328,338/473/475/479/482/
bitstring: : toggle 445/451 487/495
bitstring: :trim 445/450 default_size 42/348/358,386/395/475,
_Bitsxstr 430/435/458 481
_Boo1 67/ 463/526/ 543 <defines> 6/16-17/24/27/41-49,65/89/
Borland xiv 497
Brodie, Jim 19/580 deprecated features 16/ 119
buffered file 31/ 103/ 126/ 149/ 161/ 163/ destructor 10/34-35/52,54-55, 63/66/ 80,
220-221/ 308/ 316-319/ 324-325/ 328/ 94/133/337/340/473/475
336-337/ 340-341 domain_error 63
double construction 133/ 339/ 342/344
c double destruction 133, 339
_double_complex 507/ 510-513/ 516
C header 6-8/ 10/ 13-18/23-25/30/32-33/ 37/ doub1e_comp1ex::doub1e_comp1ex
39/41/47-48/289/568 507/515
C stream 31-32/114/ 147-148/ 159-161/ 170/ down cast 95
173/182/193/219/308/323-324/340 dynamic-cast expression 55/59/ 95/ 570
C++ header 6-7/ 13-18/ 23-24/ 41/ 43/ 45/ 568 dynarray<T> 473-492
ca1loc 31/ 86-87 dynarray<T> : : append 476-477/ 482
capacity 42,348,358,386, 395,475,481 dynarray<T> : : assign 477/481
Cartesian form 501-502/506/ 511/ 516-517/ dynarray<T>: : base 479/ 482
530-531/ 568/ 574 dynarray<T> : : dynarray 475-476/
<cassert> 14 480-481
_CATCH_IO_EXCEPTION 128/ 543 dynarray<T>: : get_at 478/ 482
<cctype> 14-15 dynarray<T> : : insert 477-478,482
<complex> 6/ 16/ 32/ 473, 501-540/545 dynarray<T>: : length 479/480
conj 505/509/514 dynarray<T>: : operator+= 476/482
conversion specification 104/ 125-126/ 178/ dynarray<T>: : operator [] 479/482
181-184/192/222/225-229/234/237/ dynarray<T>: :put_at 478/482
243/245/263/266/569-570/576/578 dynarray<T> : : remove 478/482
copy constructor 10/67/ 98-99/ 133/473/ dynarray<T> : : reserve 479/ 480
475/487/516/569 dynarray<T>: : resize 479/482
copyright xiv-x", 342 dynarray<T>: : sub_array 478/ 483
cos 505/509/514/517 dynarray<T>: : swap 478/482
cosh 505/509/514/ 517/ 526/ 530/545
_cp1usp1us 37
583
filebuf: : is_open 311
E filebuf: : open 311, 323
Edison Design Group xiv filebuf: : overflow 311, 317
Ellis, Margaret A. 579 filebuf: :pbackfail 312,317
embedded application 54, 66,266,342 filebuf: : seekoff 313,318
encapsulation 1,4,29,32,89,119,128,132, filebuf: : seekpos 314,318
570 filebuf: : setbuf 314,318
end 109, 160 filebuf: : sync 314,318
end-of-file 24,44-45, 113, 147, 150, 176, filebuf: :uflow 313,318
183-186, 188, 190-191, 215, 228, 277, filebuf: :underflow 312,317
301,322,357,427,447,570 filebuf: :xsgetn 313
endl 103,223,229,232 filebuf: :xsputn 313
ends 223,229,232 _Filet 10-11,325,328,543-544
enumerated type II, 16,42, Ill, 113, fixed 109, 117, 126, 138
121-122, 160 <float .h> 6, 16
EOF 150 _float_complex 503,516
eofbit 109, 123, 190-191, 277 float_complex: : float_complex 503,
<errno. h> 6, 13, 16 515
<exception> 6, 16,34, 48, 51-80,87-88, floatfield 109, 126, 134
98,128,267,366,403,434,458,483 flush 114, 182,221-231,234,244,314
exception handler 8,34,51,53-55, 63-67, 72, fopen 311
77,88,99, 102, 128, 175, 197-198,214, FORTRAN 501
221,239,570,577-578 fpos_t 148,150,166,309,543
exception mask 123, 127 _Fpost 166, 543
exception specification 54-55, 64, 67, 570, fprintf 225
578 fputc 312,317
exit 31-32, 66-67, 87 Free Software Foundation xiv
EXIT_FAILURE 32, 66 friend qualifier 12,250,253
EXIT_SUCCESS 32 fscanf 181
exp 502,505,509,514,517,526,531,534, fsetpos 320
546 <fstream> 6, 10, 16, 25, 108, 122, 133,
extern "C" 8, 18,37 160-161,170,188-189,221,230,267,
extern "C++" 8 307-336, 341
external linkage 3, 7-8, 10, 13-14,20, 103,
338,342,430,458,572 G
getline 173, 176, 180-181, 186, 191,212,
F 215,357,362
failbit 109,123,190 GNU, Project xi\', 571
fair use xiv-xv goodbit 109, 123
fclose 311,324
fflush 314 H
fgetc 313,317,324,329
file name 6, 13, 17-18, 21, 24, 27, 80, 320, handler function 9, 42-43, 48,53-57, 62-67,
322,366,405,571 77,81-90,94,570-578
_HAS_ARRAY_NEW 89-92, 543
file positioning 148,335,570-571, 576-577
_HAS_PJP_CLIB 329,336,543-544
file scope 8, 15
_HAS_SIGNED_CHAR 195, 235, 283, 366,
file-level declaration 17, 25
543
filebuf::-filebuf 310
_HAS_TYPEINFO 100, 543
filebuf: :close 311
hex 109,118,138,250,252
filebuf: : filebuf 310,322-325
584
ios: : good 115,123
ios: : init 117, 128, 182, 226
idempotence 17 ios: :Init: :-Init 114,338-339
ifstream::-ifstream 315 ios: :Init: : Init 114,128,338-341
ifstream: : close 315,321 ios: : io_state 16, 113, 119, 122
ifstream: : ifstream 315,320-323 ios: : ios 114, 117
ifstream: : is_open 315,321-323 ios: : iostate 16, 112, 121
ifstream: : open 315,321 ios: : iword 117, 125, 145
ifstream: : rdbuf 315,322-324 ios: : open_mode 113,122,152,154,
imag 503-505,507-509,511,513-514, 516 310-311
imanip<T> 254-257 ios: :openmode 16, 113, 121, 152, 154,
imanip<T>::imanip 255 158, 160, 268, 272,294-299, 304,
implementation type 11 309-311,313-314,316,318,321
implementation-defined 6, 9, 30, 32, 42, 64, ios : : operator! 114, 124
87, 115 ios: : operator-void* 114, 124
implementation-dependent 27,38,41,47, ios: :precision 116, 125
89,150,187,229,541 ios: :pword 117, 125, 145
implicit conversion 45, 516 ios: : rdbuf lIS, 122
in 109,311,321-322 ios: :rdstate 115,123
invalid_argument 63 ios: :seek_dir 16,113,119,122,152,154
_Inf 526,546 ios: : seekdir 16, 113, 122, 152, 154, 158,
infinity 526, 572 268,272,294,296,310,313,316,318
Informal Review Draft xi-xii, xiv-xvi, 5, 33, ios: : setf 116, 127
45,63-64,120,188,275,299,358,384, ios: : setstate 115, 123
427,545,572,580 ios: :tie 114-115,125,233
~e 13,21-22,33,36,39,72,149,162,174, ios: :unsetf 116, 127
237, 239, 242-243,253, 260, 289,304, ios: :width 116-117, 125, 189,223,231-232
329,375,411,422,430,434,483,495, ios: :xalloc 117, 125
501,523,526,528,530 <iostream> 3,6, 15-22,66, 103, 105-108,
internal 109, 118, 126, 139 114, 125, 133, 139, 143, 188, 230, 323,
<iomanip> 6, 16, 105, 108, 124, 127, 189, 337-343
232,249-263,422 iostream.h IS, 17, 251,337
<ios> 6-7, 16, 103-146, 159, 179, 188-189, ISO 5,572
197, 224, 230-231, 245, 257, 337-338, <iso646. h> 6, 14, 16, 30, 36, 546
421,473 iso_standard_library ~8,32
ios: : -ios 114 isspace 182-183,357
ios: :bad 115, 123 istdiostream::-istdiostream 319
ios: : clear 115,123 istdiostream: :buffered 319,324
ios: : copyfmt 114, 127 istdiostream: : istdiostream 318,
ios: : eof lIS, 123 323-324
ios: : exceptions 116,123 istdiostream::rdbuf 319
ios: : fail lIS, 123 <istream> 6, 16, 107, 128, 133, 173-217,
ios: : failure: : -failure 112 224,230,257,332,337,378,545
ios: : failure: : do_raise 112 istream: : -istream 182
ios: : failure: : failure 112, 121 istream: :gcount 175, 187, 190-191
ios: : fd 16 istream: :get 175-176, 185, 190-193, 215
ios: : fill 116, 124,223, 231 istream: :getline 175-176, 186, 191, 215
ios: : flags 116,125-127 istream: : ignore 177, 186, 191
ios: : fmtflags 112, 121, 127,250,252, istream: :ipfx 123,174-175,182
255-256, 421 istream: : isfx 174-175, 182,215
585
istream: : istream 182, 188-189 <math.h> 6, 16,37,39,527-528,546
istream: : operator» 105,182-184, _MAX_EXP_DIG 195, 208, 242, 543
190-192 _MAX_INT_DIG 196,201,543
istream: :peek 177, 187, 193 _MAX_BIG_DIG 196,208,242,543
istream: :putback 177, 187, 193 memchr 30, 36
istream: : read 176,186-187,191 memcmp 355, 393
istream: : sync 177, 187, 193 memory leak 80
istream: :unget 177, 187, 193 Meyers, Scott 580
istringstream: : -istringstream 298 Microsoft xi\', 573, 580
istringstream: : istringstream 298, multiple inheritance 95, 573
300-301 multithread 573
istringstream: : rdbuf 298,301 mumal independence 17
istringstream: : str 298,300
istrstream::-istrstream 273 N
istrstream: : istrstream 273-276
istrstream: : rdbuf 273, 276 name mangling 98
name space xii-xiii, 8, 19-20, 32-33,571, 573
J namespace declaration xii-x\', 19, 120
_Nan 526,546
jmp_buf 13, 31, 34 NDEBUG 7,23
<new> 6-7, 13, 16,31,53,72,81-94
K new, array 84-85
new, member 84
keyvvord 7,17,20,28,30,33,36,44,47-48, new.h 15,17
67, 383, 543-544 nifty counter 339
Koenig, Andrew xvi, 579-580 norm 505,509,514, 516
noshowbase 118, 127, 139
L noshowpoint 118, 127, 139
Lee, Meng 580 noshowpos 118, 127, 139
left 109,118,126,139 noskipws 118, 127, 139
length_error 63 not-a-number 526, 574
licensing xv nouppercase 118, 127, 139
<limits .h> 6, 16, 178, 183, 186, 190-191, ~TBS 12,14,57,225,227,229,273-274,311
269,276,300,357,424,430 ~TMBS 12, 14, 56-57,59-61, 86, 88, 96-97,
<locale.h> 6,12,16,30,32,126,193,235 99, 112
localeconv 193,235 ~TWCS 13-14
locales 6, 12, 16, 30, 32, 119, 126, 188, 193, null-terminated 4, 12-13, 96, 104, 173, 176,
230,235,568,572-573,578 178,215,223,265,275-276,279,287,
log 505,509,514,517,534 301,320,345-346,359,366,396,406,
long_double_complex 501-502,510-511, 415, 571
514-515,517,523,525,535
long_double_complex:: o
long_double_complex 511, 515 oct 109,118,126,139
longjmp 31,34-35,51-52,54,66, 77, 80-81 offsetof 31, 33-34
lo\Vercase 12,18,21,112,126,567,573 ofstream 315-316,322-323
ofstream: :close 316
M ofstream: : is_open 316,322-323
malloc 31,86-87,279 ofstream: : ofstream 316,322-323
masking macro 13, 37, 39 ofstream: : open 316,322
math functions xvi, 32, 37, SOl, 545,579 ofstream: : rdbuf 316,322-323
586
omanip<T> 255-257 ostream: : flush 221,229,232, 234
omanip<T>::omanip 255 ostream: :operator« 104, 191,227-228
One Definition Rule 17 ostream: : opfx 123, 220, 227
open file 113, 309, 321-323 ostream: : osfx 220,227,231, 245
operator! = 97-98, 150-151,356,361,394, ostream: : ostream 226,230-231
398,423,426,440,446,504-505, ostream: :put 221,228,232
508-509, 513 ostream: :write 221,229,232
operator delete 13,35, 83-90, 94 ostringstream::-ostringstream 299
operator delete [] 13, 85-89, 92 ostringstream::ostringstream
operator new 13,31,35, 82-90, 94 299-301
operator new [] 13, 85-90 ostringstream: : rdbuf 299-301
operator& II, 132, 426, 447, 451 ostringstream: : str 299-301
operator&:= II, 132, 423-424, 440, 442 ostrstream: : -ostrstream 274
operator* 504, 508, 512 ostrstream: : freeze 274,277
operator*= 502-503, 506-507, 510-511 ostrstream: : ostrstream 274-279
operator+ 1, 150-151,355-356,361, ostrstream: :pcount 275-277
393-394,398,447,450,480,483,494, ostrstream: : rdbuf 274,277-279
503-504, 507-508, 511-513 ostrstream: : str 274,277
operator+= 150-151,346,349,384,387, out 109,311,321-322
440,442,474,476,491-493,502-503, out_of_range 63
506-507, 510-511 outofrange 54, 60-61, 63, 76, 348, 386,
operator- 150-151, 503-504,507-508, 423,441,475
512-513 outofrange::-outofrange 61
operator-= 150-151,502-503,506-507, outofrange::do_raise 61
510-511 outofrange::outofrange 60
operator / 504, 508, 512 overflow::-overflow 61
operator/= 502-503,506-507,510-511 overflow: : do_raise 61,64
operator== 97,99, 150-151,356,361, 394, overflow: : overflow 61
398,423,426,440,446,504,508,513 overflow_error 63
operator" II, 132, 427, 447, 451 overload resolution 252, 422,474,571, 574
operator"= II, 132,423-424,440,442
operator I 11, 132,426,447,451 p
operator I = II, 132,423-424,440,442
operator- II, 132,423,425,440, 447 Pascal 4, 501
operator» lOS, 124-125, 178-184, performance xiii, 26-27, 51, 94, 103, 126,
189-191,196,198,201-205,208-209, 160,170,175-176,193,220-221,308,
229,254-255,357,362,423,426-427, 324,328,332,336,341,468,473,487,
440,447,451,505,509,513 528,535
operator« 104, 119, 191, 221-225, Plauger, ~J. 19,580
227-228, 231-234, 237, 239,243, Plum Hall Inc. xiv-X\T, 38, 545, 580
249-252, 254-255, 357, 362,423, Plum Thomas xiv-xvi, 580
426-427,440,446-447,451,505,509,513 polar 505,510,514,517
ostdiostream::-ostdiostream 319 polar form 502,531,535
pow 1,506, 510,514,517, 531, 534
ostdiostream: :buffered 319,324
ostdiostream: :ostdiostream 319, primary header 6-7
324 program startup 3,6,9,575,577
ostdiostream: :rdbuf 319,324 _Ptrdifft 544
<ostream> 6, 16, 107, 128, 133, 179, <ptrdynarray> 6, 16-17,45-46,249,473,
219-247,257,332,337 480, 491-499
ptrdynarray<T>::append 493
ostream: : -ostream 227
ptrdynarray<T>::assign 493
587
ptrdynarray<T>::base 494 showmany 159, 320
ptrdynarray<T>::get_at 494 showpoint 109,118,126,139,234
ptrdynarray<T>::insert 493 showpos 109,118,126,139,233-234
ptrdynarray<T>::length 494 <signal.h> 6,16
ptrdynarray<T> : : operator+= 492-493 sin 39,506,510,515,517
ptrdynarray<T>::operator[] 494 si~ 506,510,515,517,526,530,546
ptrdynarray<T>: :ptrdynarray 492 _Sizet 47, 544
ptrdynarray<T>: :put_at 494 skipws 109, 118-119, 126, 139, 189-190
ptrdynarray<T>::remove 493 smanip<T> 254-257
ptrdynarray<T>::reserve 494 smanip<T>::smanip 254
ptrdynarray<T>::resize 494 sqrt 36,506,510,515,517
ptrdynarray<T>::sub_array 493 <sstream> 6, 16, 108, 188,214,230, 267,
ptrdynarray<T>::swap 493 283, 293-306
putchar 103, 329 standard error 103,230,323,337,341-342
standard input 105-106, 173, 177, 188,323,
R 337,341,362,429,451,517
standard output 103,219,222, 230,323,
raise 8, 53, 56, 72, 77, 82, 86-87, 89, 115, 337, 341-342, 362,430,451, 517
123,348,386,424,441,475,544 Standard Template Library 427,448, 480,
range_error 63 495
real 503-511,513-514,516 static NTBS 12, 14
realloc 31, 86-87 static NTMBS 12, 97
required behavior 9 static NTWCS 13-14
_RERAISE 128,243, 544
<stdarg. h> 6, 13, 16, 33-34, 39
reserve 42,348,358,386,395,475,481 <stddef.h> 6,16,30-31,33,41-42,43,383
reserved name 7, 10, 13, 575 <stdexcept> 64
resetiosflags 127,255-257 <stdio .h> 2-3,6, 10-11, 16,24-25,29-31,
right 109, 118, 126, 139 37,44, 114, 126, 147-148, 150, 158,
RTll 95-96,98-102,120,576 160-161, 173, 175, 177, 181,219,
runtime_error 63 221-222,225,235,266,307-314,317,
320,323,325,335-336,340,383,543
5 stdiobuf::-stdiobuf 317
Saks, Dan xvi, 580 stdiobuf: :buffered 317,325
Schwarz, Jerry xv, 106, 162 stdiobuf::overflow 317
scientific 109, 118, 126, 139 stdiobuf: :pbackfail 317
secondary header 6-7, 13 stdiobuf::seekoff 318
seekg 149 stdiobuf::seekpos 318
set_new_handler 48, 86, 89 stdiobuf::setbuf 318
set_terminate 48, 62, 66 stdiobuf: : stdiobuf 317,325
set_unexpected 48,63 stdiobuf::sync 318
setbase 127,256-257 stdiobuf: :uflow 318
setfill 124,256-257 stdiobuf: : underflow 317
setiosflags 127, 255-257 stdiobuf::xsgetn 318
setjmp 6, 13, 16, 31,34-35,51-52,66, 77, stdiobuf::xsputn 318
80-81 <stdlib. h> 6, 16,30-33,35,55,62-63,
setlocale 12 66-67,86-87, 90, 94, 177, 192-193, 198,
setprecision 125, 256-257 279,366,383,405,459,545
setvbuf 314 Stepano\', A.A. 580
setw 105, 125,256-257 _Stod 208-209,215,545
showbase 109, 118, 126, 139, 233 _Stof 209,215,545
588
_Stold 209,215,545 streamoff 147, ISO-lSI, 160, 166,272,
strchr 30, 36 297,325
stream buffer 111-114, 122-124, 127, 149, streampos: :offset 151
153, 159-163, 171-172, 175-176, streampos::operator+ 151
179-180,182,187-190,193,198,210, streampos: : operator+= 151
214-215,220-225,229-230,234,243, streampos::operator- 151
245,265,267,276,278,285,288,294, streampos: : operator== 151
300,304,310,317,321-324,336-340 streampos::streampos 150
stream position 148, 150-151, 153, 157-158, <string> 6, 16, 35, 45-46, 48, 64-65, 108,
161, 271-272, 276-277, 289, 296-297, 119,293,301,345-384,399,428-429,
300-301,309,312-314,321,325 435,439,451,473
<streambuf> 6, 16, 24-25, 44, 107, 123, <string. h> 4, 6, 14, 16,30-31, 36, 39, 232,
133,147-172,179,188,220,224,230, 269,274,289,345,349,355,370,379,
309,320,337,473 383,408
streambuf::-streambuf 154 string: : append 350,359
streambuf: : eback 155 string: : assign 350, 359
streambuf: : egptr 155 string: : c_str 352, 360
streambuf: : epptr 155 string: : compare 355,361
streambuf: : gbwnp 155 string: : copy 352, 360
streambuf: : gptr 155 string: : find 353,361
streambuf: : overflow 156, 162, 270 string::find_first_not_of 354,362
streambuf: : pbackfail 156, 163, 271 string: : find_first_of 353-354,362
streambuf: : pbase 155 string: : find_last_not_of 355,362
streambuf: : pbwnp 156 string: : find_last_of 354,391-392
streambuf: :pptr 155 string: : get_at 351,360
streambuf: :pubseekoff 154, 161 string: : insert 350-351,359
streambuf: :pubsetbuf 154,161 string: : length 352, 358
streambuf: :pubsync 154, 161 string: : operator+= 349, 359
streambuf: : sbwnpc 154, 160 string: :operator= 349,359
streambuf: : seekoff 158, 272 string: : operator [] 352, 360
streambuf: : seekpos 158,272 string: :put_at 352,360
streambuf: : setbuf 158, 272, 297 string: : remove 351, 360
streambuf: : setg 155 string: : replace 351, 360
streambuf: : setp 156 string: : reserve 352, 358
streambuf: : sgetc 154, 160 string: : resize 352, 360
streambuf: : sgetn 154, 160 string: : rfind 353,362
streambuf: : snextc 154, 160 string: : string 348-349, 358
streambuf::sputbackc 155,160 string: : substr 355, 3361
streambuf: : sputc ISS, 160 stringbuf: : -stringbuf 295
streambuf: : sputn ISS, 160 stringbuf::overflow 295
streambuf::streambuf 155 stringbuf::pbackfai1296
streambuf::sungetc 155,160 stringbuf::seekoff 296
streambuf: : sync 158,273,297 stringbuf::seekpos 297
streambuf: :uflow 157,159,163,271, stringbuf: : setbuf 297
296 stringbuf: : str 295, 300
streambuf: : underflow 157, 163, 271 stringbuf: : stringbuf 295, 300
streambuf: :xsgetn 157,271,296,313, stringbuf::sync 297
318 stringbuf: :uflow 296
streambuf: :xsputn 158,271,296,313, stringbuf: : underflow 296
318 stringbuf: : xsgetn 296
589
stringbuf::xsputn 296
strlen 232,269,274,349
u
Stroustrup, Bjarne xvi, 5,579-580 unbuffered file 103, 149,221, 308, 336-337,
strpbrk 30, 36 340
strrchr 31, 36 #undef 13
strstr 31, 36 underscore character 8,20-21
<strstream> 6, 16, 108, 161, 170, 188,230, unexpected 63, 66-67
232,245,265-293,306 ungetc 152, 155, 160, 177, 187,312-313,317
strstreambuf::-strstreambuf 270 unitbuf 109, 126,231
strstreambuf::freeze 270 ~IX 148,289,308,325,575,577-578
strstreambuf: : overflow 270 unreserved name 10
strstreambuf: :pbackfail 271 uppercase 8, 12,20-21, 109, 112, 118-119,
strstreambuf::pcount 270 126-127, 139, 171,226,233-234,567,578
strstreambuf: : seekoff 272 uppercase 109, 118, 126, 139, 233-234
strstreambuf: : seekpos 272 user-defined macros 15-16
strstreambuf: : setbuf 272
strstreambuf::str 270
strstreambuf::strstreambuf
v
268-270, 279-280 va_arg 34
strstreambuf: : sync 273 va_end 13
strstreambuf: :uflow 271 va_list 13,33-34, 39
strstreambuf: : underflow 271 validation xi\', 25-27, 38
strstreambuf: : xsgetn 271 value semantics 473,480,487,491,498
strstreambuf: :xsputn 271 varying length argument list 4, 34, 39,241
strtod 177, 192-193,206, 208-209 Vilot, Mike x\', 580
strtof 206 virtual member function 9-10,54,64,95,
strtold 206,208 106, 133, 149, 159, 161-163, 170, 175,
Sun Microsystems xiv 220,267,278,288-289,309,320,325,
543
T vsprintf 235,241-242,266
x
X3J16, Committee xv, 5, 578, 580
xalloc::-xalloc 86
xalloc::do_raise 86
xalloc: :xalloc 86
xdomain::-xdomain 62
xdomain: : do_raise 62
xdomain: : xdomain 62
xlogic::-xlogic 58
xlogic::do_raise 58
xlogic : : xlogic 58
xmsg: : -xmsg 57
xmsg: : do_raise 57-62, 86, 97, 112
xmsg: : raise 57, 64, 72
xmsg::raise_handler 56
xmsg: : set_raise_handler 57, 66
xmsg: :what 57
xmsg: : where 57
xmsg: :why 57
xmsg: : xmsg 57
xrange: : -xrange 62
xrange::do_raise 62
xrange: : xrange 62
xruntime: : -xruntime 58
xruntime: :do_raise 59
xruntime: :xruntime 58-59