container_sink
Suppose you want to write a Device for appending characters to an STL container. A Device which only supports writing is called a Sink. A typical narrow-character Sink looks like this:
#include <iosfwd> // streamsize #include <boost/iostreams/categories.hpp> // sink_tag namespace io = boost::iostreams; class my_sink { public: typedef char char_type; typedef sink_tag category; std::streamsize write(const char* s, std::streamsize n) { // Write up to n characters to the underlying // data sink into the buffer s, returning the // number of characters written } /* Other members */ };
Here the member type char_type
indicates the type of characters handled by my_source, which will almost always be char
or wchar_t
. The member type category indicates which of the fundamental i/o operations are supported by the device. The category tag sink_tag
indicates that only write
is supported.
The member function write
writes up to n
character into the buffer s
and returns the number of character written. In general, write
may return fewer characters than requested, in which case the Sink is call non-blocking. Non-blocking Devices do not interact well with stanard streams and stream buffers, however, so most devices should be Blocking. See Asynchronous and Non-Blocking I/O.
You could also write the above example as follows:
#include <boost/iostreams/concepts.hpp> // sink class my_sink : public sink { public: std::streamsize write(const char* s, std::streamsize n); /* Other members */ };
Here sink
is a convenience base class which provides the member types char_type
and category
, as well as no-op implementations of member functions close
and imbue
, not needed here.
You're now ready to write your container_sink
.
#include <algorithm> // copy, min #include <iosfwd> // streamsize #include <boost/iostreams/categories.hpp> // sink_tag namespace boost { namespace iostreams { namespace example { template<typename Container> class container_sink { public: typedef typename Container::value_type char_type; typedef sink_tag category; container_sink(Container& container) : container_(container) { } std::streamsize write(const char_type* s, std::streamsize n) { container_.insert(container_.end(), s, s + n); return n; } Container& container() { return container_; } private: Container& container_; }; } } } // End namespace boost::iostreams:example
Here, note that
char_type
is defined to be equal to the containers's value_type
;
category
tells the Iostreams library that container_sink
is a model of Sink;
container_sink
can be constructed from a instance of Container
, which is passed and stored by reference, and accessible via the member function container()
; and
write()
simply appends the characters in the specified buffer to the underlying container using the container's insert
funcion,
You can write to a container_sink as follows
#include <cassert> #include <string> #include <boost/iostreams/stream.hpp> #include <libs/iostreams/example/container_device.hpp> // container_sink namespace io = boost::iostreams; namespace ex = boost::iostreams::example; int main() { using namespace std; typedef ex::container_sink<string> string_sink; string result; io::stream<string_sink> out(result); out << "Hello World!"; out.flush(); assert(result == "Hello World!"); }
Note that the Iostreams library provides buffering by default. Consequently, the stream out
must be flushed before the characters written are guaranteed to be reflected in the underlying string
.
Finally, I should mention that the Iostreams library offers easier ways to append to an STL-compatible container. First, OutputIterators can be added directly to filtering streams and stream buffers. So you could write:
#include <cassert> #include <iterator> // back_inserter #include <string> #include <boost/iostreams/filtering_stream.hpp> namespace io = boost::iostreams; int main() { using namespace std; string result; io::filtering_ostream out(back_inserter(result)); out << "Hello World!"; out.flush(); assert(result == "Hello World!"); }
Second, the Iostreams library provides a version of back_inserter
that is somewhat more efficient than std::back_inserter
because the Sink it returns uses insert
rather than push_back
. So you could write:
#include <cassert> #include <string> #include <boost/iostreams/device/back_inserter.hpp> #include <boost/iostreams/filtering_stream.hpp> namespace io = boost::iostreams; int main() { using namespace std; string result; io::filtering_ostream out(io::back_inserter(result)); out << "Hello World!"; out.flush(); assert(result == "Hello World!"); }
© Copyright 2008 CodeRage, LLC
© Copyright 2004-2007 Jonathan Turkanis
Use, modification, and distribution are subject to the Boost Software License, Version 2.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)