write
Roughly, item 4a. says that streams and stream buffers should be usable wherever standard library streams and stream buffers are usable; item 4b. says that standard library streams and stream buffers should be usable wherever Source and Sinks are usable.
The benefits of generic design are well-known. As it relates to the current library, the main benefits are these:
One benefit of generic design which is often cited but which has minimal impact on the current library is the performance boost that comes when virtual function calls are replaced by inline code. Because std::basic_streambuf
is implemented in terms of virtual functions we cannot resonably expect to eliminate virtual function calls. The cost of these calls, however, is largely mitigated by buffering. In fact, because of buffering, we might reduce code bloat by internally wrapping Filters and Devices in polymorphic classes with virtual function read
, write
, etc. without a noticable performance impact.
The large number of supported modes probably adds more than anything else to the complexity of the library. Unfortunately, all the modes seem to be necessary for a comprehesive iostreams extension framework. The examples here indicate that all but one of the modes have important use cases. The exception is bidirectional_seekable; this mode, however, essentially comes for free once the others are implemented.
I/O libraries tend to handle this situation in one of two ways:
Clearly the second alternative is unacceptable. The first alternative is attractive for its simplicity, but leaves out crucial use cases. It was judged that the library could achieve the best of both worlds simply by recommending that new users ignore all modes but input and output.
In earlier versions of this library, the chain interface was much richer. Chains had iterators and could be manipulated much like std::lists
. The class detail::chain
was part of the public interface of the library so that one could prepare a chain of Filters and use it with several streams in succession.
This functionality was removed simply to make the library easier to learn. If there is a need for it, it can easily be restored.
write
Originally, the function boost::iostreams::write
was specified to return a std::streamsize
value indicating how many characters were successfully written. The current specification, that write should throw an exception if the requested number of characters cannot be written, was adopter for these reasons:
std::streamsize
function argument in a return statement is tedious and error-prone.
This is being reconsidered in the context of alternative models. See Planned Changes and Future Directions.
For simplicity the library does not support stream positioning which is sensitive to code conversion state or streams and stream buffers which are repositional but not arbitrarily repositional in the sense of [ISO] 17.1.1 and 17.1.16. It should be straightforward to add this functionality if it is desired.
The following changes are planned, but are on hold pending review:
adapt
will be eliminated as follows. The concept of a smart Devices, which already exists internally, will be made public. Smart Devices adjust their character type and mode as they are added to a stream or stream buffer. This allows, e.g., output iterators to be treated as sinks even though their character type is not known in advance. Iterator ranges from Boost.Range, output iterators (identified using boost::detail::is_incrementable
) and standard streams and stream buffers will then be recognized as smart Devices, with no need for explicit adaptation.
There are a number of small additions to the library which might be beneficial:
template<typename Sink> void write_int32(Sink& sink, int);
read
, write
, putback
, etc.
A more ambitious extension would be to add new Device concepts for non-blocking, asynchronous and multiplexed i/o, and provide appropriate abstractions for accessing these Devices, directly and with Filter chains. In order to make this work, it is necessary to ensure that the current Filter concepts can support these alternative models, since it would be wasteful to require several versions of each Filter to accommodate the various models. The main impediment is that Filters must be able to indicate that the number of characters written or read was fewer than requested, although end-of-sequence has not been reached and no error has occurred. There are several ways this could be achieved:
read
return 0 to indicate end-of-sequence and let all other return values for read
and write
indicate the actual number of characters processed. get
and put
will always block.
read
return -1 to indicate end-of-sequence, and let all other return values, including 0, indicate the actual number of characters read or written. Let put
return a bool
indicating whether a character was written. Let the return type of get
be a class type which can store a character, an end-of-sequence indicator or a 'try back later' indicator. E.g.,
template<typename Ch> struct basic_character { enum nochar { eof, unavail }; basic_character(Ch c); basic_character(nochar type); operator Ch () const; // Tests whether value represents a character. operator safe_bool() const; bool operator!() const; bool eof() const; bool unavail() const; ... }; typedef basic_character<char> character; typedef basic_character<wchar_t> wcharacter; character eof() { return character(character::eof); } wcharacter weof() { return wcharacter(character::eof); } character unavail() { return character(character::unavail); } wcharacter wunavail() { return wcharacter(character::unavail); }Using
basic_character
, writing InputFilters would be almost the same as it is currently.
None of these is entirely satisfactory. Item 1 allows performace to suffer — especially if Filters are involved — just to accommodate an insufficiently expressive interface. Item 2 makes writing Filters for ordinary blocking i/o slightly more complex, and may have an abstraction penalty. Item 3 is obviously unworkable.
[1]Informal tests conducted by the author showed stream<file_descriptor_sink>
to be slightly faster than the Dinkumware implementation of std::ofstream which ships with Microsoft Visual Studio .NET 2003. Note, however, that a file_descriptor_sink
performs no locking. Similarly, an ostringstream
constructed with the Boost Iostreams library performed comparably to the Dinkumware implementation of std::stringstream. No tests of input or random access have been performed yet.
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)