Rationale

Contents

Design Goals
Simplicity
Flexibility
Efficiency
Interoperability
Design Decisions
Generic Design
Modes
Lifetime Management
Chain Interface
Asynchronous and Non-Blocking I/O

Design Goals

Four criteria shaped the design of Boost.Iostreams:

  1. Simplicity: The library should be easy to learn, use and extend.
  2. Flexibility: The library should be flexible enough to produce streams and stream buffers with almost any desired characteristics.
  3. Efficiency: Streams and stream buffers produced using the library should be as efficient as hand-written components, in most cases.
  4. Interoperability: The library should fit easily within the framework of the C++ standard library. In particular,
    1. The components used to construct streams and stream buffers — Source, Sinks, InputFilters and OutputFilters — should not requirer a richer interface than standard library streams and stream buffers.
    2. Streams and stream buffers constructed using the library should not require a richer interface than standard library streams and stream buffers.

Essentially, 4a says that standard library streams and stream buffers should be usable wherever Source and Sinks are usable, while 4b says that says that streams and stream buffers should be usable wherever standard library streams and stream buffers are usable. Both guidelines aim to make it easy to integrate Boost.Iostreams into existing code.

Simplicity

Many users have reported that the library is easy to learn and use. It has also proved to be relatively easy to extend. The need for flexibility and efficiency has complicated the design, however. A good example is the large number of modes required to represent the full range of streams and stream buffers. Another example is the need optimize the treatment of Devices representing in-memory character sequences, such as memory-mapped files; this led to the introduction of Direct Devices, which are somewhat harder to use than other Devices.

Flexibility

Although Boost.Iostreams is extremely flexible, it has some limitations. For example, it is not possible to customize the buffering strategy used by stream_buffer: the user must either choose the default buffering strategy or request unbuffered i/o. Giving the user complete control over the buffering strategy would substantially complicated the library interface and implementation.

Efficiency

Preliminary measurements indicate that the streams and stream buffers constructed using Boost.Iostreams perform comparably to hand-written components. Further measurements — and appropriate adjustments to the library implementation — must await the establishment of a comprehensive set of benchmarks.

Interoperability

Standard library streams and stream buffers can currently be used wherever a Device is allowed; this will change, however, when more support for asynchronous and non-blocking i/o is added, since streams and stream buffers are unable to distinguish between temporary and permanent failures to satsify a read or write request. Furthermore, streams and stream buffers defined using Boost.Iostreams can be used wherever a standard library stream or stream buffer is required, but only after the stream or stream buffer has been properly initialized. For example, a user of a filtering_stream must push a sequence of Filters and a Device onto the underlying filter chain before the filtering_stream can be used by existing code. This limitation is unavoidable, however, as all streams and stream buffers require some specialized initialization, including std::fstream and std::stringstream.

Design Decisions

Generic Design

The benefits of generic design are well-known. For example,

One typical benefit of generic design that is neglible in the present case is the performance gain that comes when virtual function calls are replaced by inline code: because std::basic_streambuf uses virtual functions as customization points, the library cannot resonably expect to eliminate virtual function calls. The cost of these calls, however, is largely mitigated by buffering.

Modes

The number of supported modes adds adds greatly to the complexity of the library. Unfortunately, all the modes seem to be necessary to satisfy the flexibility criterion. The examples here indicate that all but one of the modes have important use cases. The exception is bidirectional seekable; this mode, however, is essentially free once the others are implemented.

I/o libraries tend to handle this situation in one of two ways:

The second alternative is clearly unacceptable. The first alternative is attractive for its simplicity, but leaves out crucial use cases. Boost.Iostreams tries to achieve the best of both worlds by recommending that new users ignore all modes but input and output.

Lifetime Management

Filters and Devices must either be CopyConstructible or be passed to streams and stream buffers using boost::ref. This requirement can complicate the design of Filters and Devices, since some components that could otherwise be non-copyable must use reference counting. The template basic_file is a good illustration. A pre-release version of Boost.Iostreams allowed dynamically allocated Filters and Devices to be passed to streams and stream buffers as pointers that would become owned by the Iostreams library at the user's option. This design was rejected for two reasons: it was not exception safe, and it required an extra function parameter to indicate whether an object was to become owned by the library.

Chain Interface

Some pre-release versions of Boost.Iostreams provided filter chains with a rich interface that allowed chains to be disconnected and reattached at arbitrary positions, much like std::list. This functionality was removed to make the library easier to learn; if users find a need for it, it can easily be restored.

Asynchronous and Non-Blocking I/O

The Filter concepts provided by Boost.Iostreams have been designed to accommodate asynchronous and non-blocking i/o. The Device concepts can accommodate non-blocking i/o, but support for asynchronous i/o will require the introdocution of new Device concepts. This limited support has been added for forward compatibility: when additional concepts and components are introduced, users should not have to redesign their existing components. Furthermore, if asynchronous and non-blocking i/o turns out not to be sufficiently useful, the internal library support can be removed without affecting existing interfaces.