1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<TITLE>Tutorial</TITLE>
<LINK REL="stylesheet" HREF="../../../../boost.css">
<LINK REL="stylesheet" HREF="../theme/iostreams.css">
</HEAD>
<BODY>
<!-- Begin Banner -->
<H1 CLASS="title">Tutorial</H1>
<HR CLASS="banner">
<!-- End Banner -->
<!-- Begin Nav -->
<DIV CLASS='nav'>
<A HREF='container_sink.html'><IMG BORDER=0 WIDTH=19 HEIGHT=19 SRC='../../../../doc/html/images/prev.png'></A>
<A HREF='tutorial.html'><IMG BORDER=0 WIDTH=19 HEIGHT=19 SRC='../../../../doc/html/images/up.png'></A>
<A HREF='writing_filters.html'><IMG BORDER=0 WIDTH=19 HEIGHT=19 SRC='../../../../doc/html/images/next.png'></A>
</DIV>
<!-- End Nav -->
<A NAME="container_device"></A>
<H2>2.1.4. Writing a <CODE>container_device</CODE></H2>
<P>Suppose you want to write a Device for reading from and writing to an STL container. In order for combined reading and writing to be useful, you will also need to support seeking within the container. There are several types of Devices which combine reading and writing; they differ according to whether there are two separate character sequences for input and output, or a single combined sequence, and whether there are separate position indicators for reading and writing or a single read/write position indicator. <I>See</I> <A HREF="../guide/modes.html">Modes</A> for details.</P>
<P>A narrow-character Device for read-write access to a single character sequence with a single position indicator is called a <A HREF="../concepts/seekable_device.html">SeekableDevice</A>. A typical SeekableDevice looks like this:</P>
<PRE CLASS="broken_ie"><SPAN CLASS='preprocessor'>#include</SPAN> <SPAN CLASS="literal"><iosfwd></SPAN> <SPAN CLASS='comment'>// streamsize, seekdir</SPAN>
<SPAN CLASS='preprocessor'>#include</SPAN> <A CLASS="HEADER" HREF="../../../../boost/iostreams/categories.hpp"><SPAN CLASS='literal'><boost/iostreams/categories.hpp></SPAN></A> <SPAN CLASS='comment'>// seekable_device_tag</SPAN>
<SPAN CLASS='preprocessor'>#include</SPAN> <A CLASS="HEADER" HREF="../../../../boost/iostreams/positioning.hpp"><SPAN CLASS='literal'><boost/iostreams/positioning.hpp></SPAN></A> <SPAN CLASS='comment'>// stream_offset</SPAN>
<SPAN CLASS='keyword'>namespace</SPAN> io = boost::iostreams;
<SPAN CLASS='keyword'>class</SPAN> my_device {
<SPAN CLASS='keyword'>public</SPAN>:
<SPAN CLASS='keyword'>typedef</SPAN> <SPAN CLASS='keyword'>char</SPAN> char_type;
<SPAN CLASS='keyword'>typedef</SPAN> seekable_device_tag category;
std::streamsize read(<SPAN CLASS='keyword'>char</SPAN>* s, std::streamsize n)
{
<SPAN CLASS='comment'>// Read up to n characters from the underlying data source</SPAN>
<SPAN CLASS='comment'>// into the buffer s, returning the number of characters</SPAN>
<SPAN CLASS='comment'>// read; return -1 to indicate EOF</SPAN>
}
std::streamsize write(const <SPAN CLASS='keyword'>char</SPAN>* s, std::streamsize n)
{
<SPAN CLASS='comment'>// Write up to n characters to the underlying </SPAN>
<SPAN CLASS='comment'>// data sink into the buffer s, returning the </SPAN>
<SPAN CLASS='comment'>// number of characters written</SPAN>
}
stream_offset seek(stream_offset off, std::ios_base::seekdir way)
{
<SPAN CLASS='comment'>// Seek to position off and return the new stream </SPAN>
<SPAN CLASS='comment'>// position. The argument way indicates how off is</SPAN>
<SPAN CLASS='comment'>// interpretted:</SPAN>
<SPAN CLASS='comment'>// - std::ios_base::beg indicates an offset from the </SPAN>
<SPAN CLASS='comment'>// sequence beginning </SPAN>
<SPAN CLASS='comment'>// - std::ios_base::cur indicates an offset from the </SPAN>
<SPAN CLASS='comment'>// current character position </SPAN>
<SPAN CLASS='comment'>// - std::ios_base::end indicates an offset from the </SPAN>
<SPAN CLASS='comment'>// sequence end </SPAN>
}
<SPAN CLASS='comment'>/* Other members */</SPAN>
};</PRE>
<P>Here the member type <A HREF="../guide/traits.html#char_type"><CODE>char_type</CODE></A> indicates the type of characters handled by my_source, which will almost always be <CODE>char</CODE> or <CODE>wchar_t</CODE>. The member type <A HREF="../guide/traits.html#char_type">category</A> indicates which of the fundamental i/o operations are supported by the device. The category tag <A HREF="../guide/traits.html#category_tags"><CODE>seekable_tag</CODE></A> indicates that <A HREF="../functions/read.html"><CODE>read</CODE></A>, <A HREF="../functions/write.html"><CODE>write</CODE></A> and the single-sequence version of <A HREF="../functions/seek.html"><CODE>seek</CODE></A> are supported.</P>
<P>The type <A HREF="../functions/positioning.html#synopsis"><CODE>stream_offset</CODE></A> is used by the Iostreams library to hold stream offsets.</P>
<P>You could also write the above example as follows:</P>
<PRE CLASS="broken_ie"><SPAN CLASS='preprocessor'>#include</SPAN> <A CLASS="HEADER" HREF="../../../../boost/iostreams/concepts.hpp"><SPAN CLASS='literal'><boost/iostreams/concepts.hpp></SPAN></A> <SPAN CLASS='comment'>// seekable_device</SPAN>
<SPAN CLASS='keyword'>class</SPAN> my_source : <SPAN CLASS='keyword'>public</SPAN> seekable_device {
<SPAN CLASS='keyword'>public</SPAN>:
std::streamsize read(<SPAN CLASS='keyword'>char</SPAN>* s, std::streamsize n);
std::streamsize write(const <SPAN CLASS='keyword'>char</SPAN>* s, std::streamsize n);
stream_offset seek(stream_offset off, std::ios_base::seekdir way);
<SPAN CLASS='comment'>/* Other members */</SPAN>
};</PRE>
<P>Here <A HREF="../classes/device.html#synopsis"><CODE>seekable_device</CODE></A> is a convenience base class which provides the member types <CODE>char_type</CODE> and <CODE>category</CODE>, as well as no-op implementations of member functions <CODE>close</CODE> and <CODE>imbue</CODE>, not needed here.
<P>You're now ready to write your <CODE>container_device</CODE>. Again, let's assume your container's iterators are RandomAccessIterators.</P>
<PRE CLASS="broken_ie"><SPAN CLASS='preprocessor'>#include</SPAN> <SPAN CLASS='literal'><algorithm></SPAN> <SPAN CLASS='comment'>// copy, min</SPAN>
<SPAN CLASS='preprocessor'>#include</SPAN> <SPAN CLASS='literal'><iosfwd></SPAN> <SPAN CLASS='comment'>// streamsize</SPAN>
<SPAN CLASS='preprocessor'>#include</SPAN> <A CLASS='header' HREF="../../../../boost/iostreams/categories.hpp"><SPAN CLASS='literal'><boost/iostreams/categories.hpp></SPAN></A> <SPAN CLASS='comment'>// source_tag</SPAN>
<SPAN CLASS='keyword'>namespace</SPAN> boost { <SPAN CLASS='keyword'>namespace</SPAN> iostreams { <SPAN CLASS='keyword'>namespace</SPAN> example {
<SPAN CLASS='keyword'>template</SPAN><<SPAN CLASS="keyword">typename</SPAN> Container>
<SPAN CLASS="keyword">class</SPAN> container_device {
<SPAN CLASS="keyword">public</SPAN>:
<SPAN CLASS="keyword">typedef</SPAN> <SPAN CLASS="keyword">typename</SPAN> Container::value_type char_type;
<SPAN CLASS="keyword">typedef</SPAN> seekable_device_tag category;
container_device(Container& container)
: container_(container), pos_(<SPAN CLASS='numeric_literal'>0</SPAN>)
{ }
std::streamsize read(char_type* s, std::streamsize n)
{
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
streamsize amt = <SPAN CLASS="keyword">static_cast</SPAN><streamsize>(container_.size() - pos_);
streamsize result = (min)(n, amt);
<SPAN CLASS="keyword">if</SPAN> (result != <SPAN CLASS='numeric_literal'>0</SPAN>) {
std::copy( container_.begin() + pos_,
container_.begin() + pos_ + result,
s );
pos_ += result;
<SPAN CLASS="keyword">return</SPAN> result;
} <SPAN CLASS="keyword">else</SPAN> {
<SPAN CLASS="keyword">return</SPAN> <SPAN CLASS='numeric_literal'>-1</SPAN>; <SPAN CLASS='comment'>// EOF</SPAN>
}
}
std::streamsize write(<SPAN CLASS="keyword">const</SPAN> char_type* s, std::streamsize n)
{
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
streamsize result = <SPAN CLASS='numeric_literal'>0</SPAN>;
<SPAN CLASS="keyword">if</SPAN> (pos_ != container_.size()) {
streamsize amt =
<SPAN CLASS="keyword">static_cast</SPAN><streamsize>(container_.size() - pos_);
streamsize result = (min)(n, amt);
std::copy(s, s + result, container_.begin() + pos_);
pos_ += result;
}
<SPAN CLASS="keyword">if</SPAN> (result < n) {
container_.insert(container_.end(), s, s + n);
pos_ = container_.size();
}
<SPAN CLASS="keyword">return</SPAN> n;
}
stream_offset seek(stream_offset off, std::ios_base::seekdir way)
{
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
<SPAN CLASS='comment'>// Determine new value of pos_</SPAN>
stream_offset next;
<SPAN CLASS="keyword">if</SPAN> (way == ios_bas::beg) {
next = off;
} <SPAN CLASS="keyword">else</SPAN> <SPAN CLASS="keyword">if</SPAN> (way == ios_bas::cur) {
next = pos_ + off;
} <SPAN CLASS="keyword">else</SPAN> <SPAN CLASS="keyword">if</SPAN> (way == ios_bas::end) {
next = container_.size() + off - <SPAN CLASS='numeric_literal'>1</SPAN>;
}
<SPAN CLASS='comment'>// Check for errors</SPAN>
<SPAN CLASS="keyword">if</SPAN> (next < <SPAN CLASS='numeric_literal'>0</SPAN> || next >= container_.size())
<SPAN CLASS="keyword">throw</SPAN> ios_base::failure("bad seek offset");
pos_ = next;
<SPAN CLASS="keyword">return</SPAN> pos_;
}
Container& container() { <SPAN CLASS="keyword">return</SPAN> container_; }
<SPAN CLASS="keyword">private</SPAN>:
<SPAN CLASS="keyword">typedef</SPAN> <SPAN CLASS="keyword">typename</SPAN> Container::size_type size_type;
Container& container_;
size_type pos_;
};
} } } <SPAN CLASS='comment'>// End namespace boost::iostreams:example</SPAN></PRE>
<P>Here, note that</P>
<UL>
<LI>The member type <CODE>char_type</CODE> is defined to be equal to the containers's <CODE>value_type</CODE>;
<LI>The member type <CODE>category</CODE> tells the Iostreams library that <CODE>container_device</CODE> is a model of <A HREF="../concepts/seekable_device.html">SeekableDevice</A>;
<LI>A <CODE>container_device</CODE> can be constructed from a instance of <CODE>Container</CODE>, which is passed and stored by reference, and accessible <I>via</I> the member function <CODE>container()</CODE>;
<LI>The implementation of <CODE>read</CODE> is identical to the implementation in <A HREF="container_source.html"><CODE>container_source</CODE></A>.
</UL>
<P>The implementation of <CODE>write</CODE> is a bit different from the implementation in <A HREF="container_sink.html"><CODE>container_sink</CODE></A>. Rather than simply appending characters to the container, you first check whether the current character position is somewhere in the middle of the container. If it is, you attempt to satisfy the write request by overwriting existing characters in the conatiner, starting at the current character position. If you reach the end of the container before the write request is satisfied, you insert the remaining characters at the end.</P>
<P>The implementation of <CODE>seek</CODE> is striaghforward. First, you calculate the new character position based on <CODE>off</CODE> and <CODE>way</CODE>: if <CODE>way</CODE> is <CODE>ios_base::beg</CODE>, the new character position is simply <CODE>off</CODE>; if <CODE>way</CODE> is <CODE>ios_base::cur</CODE>, the new character position is <CODE>pos_ + off</CODE>; if <CODE>way</CODE> is <CODE>ios_base::end</CODE>, the new character position is <CODE>container_.size() + off - 1</CODE>. Next, you check whether the new character position is a valid offset, and throw an exception if it isn't. Instances of <CODE>std::basic_streambuf</CODE> are allowed to return <CODE>-1</CODE> to indicate failure, but the policy of the Boost Iostreams library is that errors should be indicated by throwing an exception (<I>see</I> <A HREF="../guide/exceptions.html">Exceptions</A>). Finally, you set the new position and return it.</P>
<P>You can use a container_device as follows</P>
<PRE CLASS="broken_ie"><SPAN CLASS='preprocessor'>#include</SPAN> <SPAN CLASS='literal'><cassert></SPAN>
<SPAN CLASS='preprocessor'>#include</SPAN> <SPAN CLASS='literal'><ios></SPAN> <SPAN CLASS='comment'>// ios_base::beg</SPAN>
<SPAN CLASS='preprocessor'>#include</SPAN> <SPAN CLASS='literal'><string></SPAN>
<SPAN CLASS='preprocessor'>#include</SPAN> <A CLASS="header" HREF="../../../../boost/iostreams/stream.hpp"><SPAN CLASS='literal'><boost/iostreams/stream.hpp></SPAN></A>
<SPAN CLASS='preprocessor'>#include</SPAN> <A CLASS="header" HREF="../../example/container_device.hpp"><SPAN CLASS='literal'><libs/iostreams/example/container_device.hpp></SPAN></A>
<SPAN CLASS="keyword">namespace</SPAN> io = boost::iostreams;
<SPAN CLASS="keyword">namespace</SPAN> ex = boost::iostreams::example;
<SPAN CLASS="keyword">int</SPAN> main()
{
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
<SPAN CLASS="keyword">typedef</SPAN> ex::container_device<string> string_device;
string one, two;
io::stream<string_device> io(one);
io << <SPAN CLASS='literal'>"Hello World!"</SPAN>;
io.flush();
io.seekg(0, BOOST_IOS::beg); <SPAN CLASS='comment'>// seek to the beginning</SPAN>
getline(io, two);
assert(one == <SPAN CLASS='literal'>"Hello World!"</SPAN>);
assert(two == <SPAN CLASS='literal'>"Hello World!"</SPAN>);
}</PRE>
<!-- Begin Nav -->
<DIV CLASS='nav'>
<A HREF='container_sink.html'><IMG BORDER=0 WIDTH=19 HEIGHT=19 SRC='../../../../doc/html/images/prev.png'></A>
<A HREF='tutorial.html'><IMG BORDER=0 WIDTH=19 HEIGHT=19 SRC='../../../../doc/html/images/up.png'></A>
<A HREF='writing_filters.html'><IMG BORDER=0 WIDTH=19 HEIGHT=19 SRC='../../../../doc/html/images/next.png'></A>
</DIV>
<!-- End Nav -->
<!-- Begin Footer -->
<HR>
<P CLASS="copyright">Revised
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
20 May, 2004
<!--webbot bot="Timestamp" endspan i-checksum="38504" -->
</P>
<P CLASS="copyright">© Copyright <A HREF="http://www.kangaroologic.com" TARGET="_top" TARGET="_top">Jonathan Turkanis</A>, 2004</P>
<P CLASS="copyright">
Use, modification, and distribution are subject to the Boost Software License, Version 2.0. (See accompanying file <A HREF="../../../../LICENSE_1_0.txt">LICENSE_1_0.txt</A> or copy at <A HREF="http://www.boost.org/LICENSE_1_0.txt">http://www.boost.org/LICENSE_1_0.txt</A>)
</P>
<!-- End Footer -->
</BODY>
|