Four criteria shaped the design of Boost.Iostreams:
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.
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.
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.
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.
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
.
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.
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.
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.
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.
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.
Revised 02 Feb 2008
© Copyright 2008 CodeRage, LLC
© Copyright 2004-2007 Jonathan Turkanis
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)