Exceptions

# User's Guide

## 3.10 Exceptions

The Standard Iostreams library
The Boost Iostreams library
Exception Safety
Acknowledgments

## The Standard Iostreams library

The `public` member functions of `std::basic_streambuf` which perform i/o — `sgetn()`, `sputn()`, `pubsync(), etc.` — are implemented using the `protected virtual` interface of `std::basic_streambuf` — `underflow()`, `overflow()`, `sync()`, etc. Most of these `protected virtual` functions are allowed to signal failure either by throwing an exception or by returning an error indicator (often `traits_type::eof()`). All exceptions thrown are propogated by the `public` member functions, so users of raw stream buffers must in principle be prepared to handle exceptions. However, the stream buffers which ship with most standard library implementations do not throw exceptions.

Standard streams, by contrast, throw no exceptions by default: users must set the exception mask to request that exceptional conditions be reported using exceptions. If `ios_base::badbit` is set in a stream's exception mask, all exceptions thrown by the underlying stream buffer will be caught and rethrown.

With streams, therefore, the user has a choice whether to enable exceptions; with stream buffers, the implementor has a choice, but the user has none.

## The Boost Iostreams library

##### Policy

The fundamental stream buffer template in the Boost Iostreams library, `stream_buffer`, implements the `protected virtual` memeber functions of `std::basic_streambuf` by delegating to the i/o functions `read`, `write`, `put`, `get`, etc., which in turn delegate to member functions of Filters and Devices. These member functions throw exceptions to report errors. The exceptions are propagated by the `protected virtual` members of `stream_buffer` and by the public members of `std::basic_streambuf`.

It is recommended that all exceptions thrown by user-defined Filters and Devices derive from `std::ios_base::failure`.[1]

##### Rationale

An alternative formulation would have been to require Filter and Device member functions with return type `std::streamsize` to return `-1` to indicate errors, and to modify the specification of functions returning `void` so that they return a `bool` indicating success. The decision to rely on exceptions to report errors was made to allow implementors a means to convey aditional information about the cause of errors, and to free them from having to remember which return values are designated as error indicators.

A third possibility would have been to follow the example of `std::basic_streambuf` and allow member functions of Filters and Devices to signal errors either by throwing exceptions or by returning designated error indicators. This was rejected because it would have complicated the specifications of the various Filter and Device concepts and made the internals of `stream_buffer` more difficult to understand and maintain.[2]

## Exception Safety

The Iostreams library aims to provide to following guarantees if an exception is thrown during an i/o operation:

1. Resources are freed by calling destructors or `close`, if appropriate.
2. The invariants specified by the C++ standard library for streams and stream buffers are maintained. Attempting to perform additional i/o is allowed but may cause further exceptions.
3. The invariants specified by the Boost Iostreams library for streams and stream buffers are maintained. Specifically,
   1. A `stream_buffer` or `stream` can be closed and reopened to perform additional i/o with a new instance of the underlying Filter or Device type.
   2. A `filtering_streambuf`'s or `filtering_stream`'s chain of Filters and Devices is in a consistent state. If the current Device is removed from the chain and a new one added the user may perform i/o as if the the stream or stream buffer were newly constructed.

Conditions 2. and 3. rely on the specification of the Filter and Device concepts. Note that there is no guarantee that a stream's or stream buffer's character sequences are in a consistent state, i.e., that data has not been corrupted, and no way in general to determine the state of these sequences after an exception.

These conditions amount to what is known as the basic guarantee of exception safety (see [Abrahams1].)

## Acknowledgments

Thanks to Angelika Langer and John Torjo for discussion of exceptions.

[1]The C++ standard describes `std::ios_base::failure` as "the base class for the types of all objects thrown as exceptions, by functions in the Iostreams library, to report errors detected during stream buffer operations." ([ISO], section 27.4.2.1.1.) There is a difference of opinion among commentators whether this means that all exceptions thrown by stream buffer member functions must be of type `failure`.

A leading text on C++ iostreams interprets the language in the standard to mean that exceptions "may be of any type" but that "if the exception is rasied due to an error situation discovered by any of the IOStreams operations, it is of the type `failure`" ([Langer], p. 36, emphasis added). As an example, the authors state that a `std::bad_alloc` generated during buffer allocation need not be caught and rethrown as a `failure`.

Another leading text, however, takes the view that all exceptions must derive from `failure`. ([Josuttis1], p. 602.)

The Iostreams library takes a neutral position on this matter: all exceptions thrown internally by the library are of type `failure` or a derived class, but exceptions thrown by user-defined types are propogated as-is.

[2]As Herb Sutter writes, "it is always poor design to allow an operation to report the same error in two different ways....It complicates the interface and the semantics. And it makes the caller's life harder because the caller must be able to handle both flavors of error reporting." ([Sutter], p. 130.)

Revised 20 May, 2004

© Copyright Jonathan Turkanis, 2004

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)