mirror of
https://github.com/boostorg/iostreams.git
synced 2026-02-22 15:32:20 +00:00
324 lines
22 KiB
HTML
Executable File
324 lines
22 KiB
HTML
Executable File
<!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 -->
|
|
|
|
<DL CLASS="page-index">
|
|
<DT><A HREF="#tutorial_source">Defining streams and stream buffers for a new data source</A></DT>
|
|
<DT><A HREF="#tutorial_sink">Defining streams and stream buffers for a new data sink</A></DT>
|
|
<DT><A HREF="#tutorial_input_filter">Filtering input</A></DT>
|
|
<DT><A HREF="#tutorial_output_filter">Filtering output</A></DT>
|
|
<DT><A HREF="#tutorial_multi_char_filters">Efficient Filtering: Multi-Character Filters</A></DT>
|
|
</DL>
|
|
|
|
<A NAME="tutorial_source"></A>
|
|
<H4>Defining Streams and Stream Buffers for a New Data Source</H4>
|
|
|
|
<P>
|
|
Suppose we want to define a standard stream buffer to read from a new type of input device. Typically this means deriving a new class from a specialization of <CODE>std::basic_streambuf</CODE> and overriding the <CODE>protected virtual</CODE> function <CODE>underflow</CODE>, and possibly also <CODE>uflow</CODE> and <CODE>xsgetn</CODE>. If we wish to be able to put back characters to the stream buffer, we also need to override <CODE>pbackfail</CODE>. If we wish to add buffering, we need to allocate a buffer and use the buffer manipulation functions <CODE>setg</CODE>, <CODE>eback</CODE>, <CODE>gptr</CODE>, <CODE>egptr</CODE> and <CODE>gbump</CODE>.
|
|
</P>
|
|
|
|
<P>
|
|
This process can be confusing to those who do not routinely work with the <CODE>protected virtual</CODE> interface of <CODE>std::basic_streambuf</CODE>. More importantly, most of the work involved has very little to do with the new
|
|
data source we wish to access. Very often, the relevant behavior of a new data source can be represented by a single member
|
|
function, prototyped as follows:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> std::streamsize read(<SPAN CLASS="keyword">char</SPAN>* s, std::streamsize n);</PRE>
|
|
<P>
|
|
The function reads up to <CODE>n</CODE> characters into the buffer <CODE>s</CODE>, returning the number of characters read and throwing exceptions to indicate errors. If an integer less than <CODE>n</CODE> is
|
|
returned, the end of the stream has been reached.
|
|
</P>
|
|
|
|
<P>
|
|
The Iostreams Library allows users to create a standard input stream or stream buffer by defining a classes
|
|
with a single member function <CODE>read</CODE> and several member types.
|
|
Buffering and the ability to put back characters come for free. For a simple example, suppose we want to define a stream buffer to read a random sequence of characters.
|
|
</P>
|
|
|
|
<P>We first define a <A HREF="concepts/source.html">Source</A> as follows:</P>
|
|
|
|
<PRE CLASS="broken_ie"> <SPAN CLASS="preprocessor">#include</SPAN> <SPAN CLASS="literal"><cstdlib></SPAN>
|
|
<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">// source</SPAN>
|
|
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> boost::io;
|
|
|
|
<SPAN CLASS="keyword">struct</SPAN> random_source : <SPAN CLASS="keyword">public</SPAN> source {
|
|
random_source(<SPAN CLASS="keyword">unsigned</SPAN> <SPAN CLASS="keyword">int</SPAN> seed = <SPAN CLASS="numeric_literal">0</SPAN>) { srand(seed); }
|
|
std::streamsize read(<SPAN CLASS="keyword">char</SPAN>* s, std::streamsize n)
|
|
{
|
|
<SPAN CLASS="keyword">for</SPAN> (streamsize z = <SPAN CLASS="numeric_literal">0</SPAN>; z < n; ++z)
|
|
s[z] = (<SPAN CLASS="keyword">char</SPAN>) (rand() * <SPAN CLASS="numeric_literal">256</SPAN> / RAND_MAX);
|
|
<SPAN CLASS="keyword">return</SPAN> n;
|
|
}
|
|
};</PRE>
|
|
|
|
<P>
|
|
Here <A HREF="classes/device.html#synopsis"><CODE>source</CODE></A> is a convenience base class which provides several
|
|
member types as well as default implementations of several member functions. (The member functions are not needed here.) Given the above definition, we can construct a standard stream buffer as follows:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> streambuf_facade<random_source> in(random_source(<SPAN CLASS="numeric_literal">5</SPAN>));
|
|
<SPAN CLASS="omitted">...</SPAN> <SPAN CLASS="comment">// read from stream</SPAN>
|
|
in.close(); <SPAN CLASS="comment">// close stream</SPAN>
|
|
in.open(random_source(<SPAN CLASS="numeric_literal">634875</SPAN>)); <SPAN CLASS="comment">// use a new seed</SPAN>
|
|
<SPAN CLASS="omitted">...</SPAN> <SPAN CLASS="comment">// read from stream</SPAN></PRE>
|
|
|
|
<P>We can also define a standard input stream:</P>
|
|
<PRE CLASS="broken_ie"> stream_facade<random_source> in(random_source(<SPAN CLASS="numeric_literal">5</SPAN>));</PRE>
|
|
|
|
<P>
|
|
Both <CODE>streambuf_facade</CODE> and <CODE>stream_facade</CODE> have constructors and overloads of open which forward their arguments to Filter or Device constructor. Therefore, the first example could have been written:
|
|
</P>
|
|
|
|
<PRE CLASS="broken_ie"> streambuf_facade<random_source> in(<SPAN CLASS="numeric_literal">5</SPAN>);
|
|
<SPAN CLASS="omitted">...</SPAN>
|
|
in.close();
|
|
in.open(<SPAN CLASS="numeric_literal">634875</SPAN>);
|
|
<SPAN CLASS="omitted">...</SPAN></PRE>
|
|
|
|
<A NAME="tutorial_sink"></A>
|
|
<H4>Defining Streams and Stream Buffers for a New Data Sink</H4>
|
|
|
|
<P>
|
|
The case is similar if we wish to define a standard stream buffer to write to a new type of output device. Instead of
|
|
overriding the <CODE>protected virtual</CODE> functions <CODE>overflow</CODE>, <CODE>xsputn</CODE> and <CODE>sync</CODE> and making use of the buffer manipulation functions <CODE>setp</CODE>, <CODE>pbase</CODE>, <CODE>pptr</CODE>, <CODE>epptr</CODE> and <CODE>pbump</CODE>, we simply define a class with several member types and a member function <CODE>write</CODE> prototyped as follows:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> <SPAN CLASS="keyword">void</SPAN> write(<SPAN CLASS="keyword">const</SPAN> <SPAN CLASS="keyword">char</SPAN>* s, std::streamsize n);</PRE>
|
|
<P>
|
|
The function writes <CODE>n</CODE> characters to the output device, throwing exceptions to indicate errors.
|
|
For a simple example, suppose we want to define a stream buffer to write characters to the end of a standard <CODE>vector</CODE>. We can define a <A HREF="concepts/sink.html">Sink</A> as follows:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> <SPAN CLASS="preprocessor">#include</SPAN> <SPAN CLASS="literal"><vector></SPAN>
|
|
<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">// sink</SPAN>
|
|
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> boost::io;
|
|
|
|
<SPAN CLASS="keyword">struct</SPAN> vector_sink : <SPAN CLASS="keyword">public</SPAN> sink {
|
|
vector_sink(vector<<SPAN CLASS="keyword">char</SPAN>>& vec) : vec(vec) { }
|
|
<SPAN CLASS="keyword">void</SPAN> write(<SPAN CLASS="keyword">const</SPAN> <SPAN CLASS="keyword">char</SPAN>* s, std::streamsize n)
|
|
{
|
|
vec.insert(vec.end(), s, s + n);
|
|
}
|
|
vector<<SPAN CLASS="keyword">char</SPAN>>& vec;
|
|
};</PRE>
|
|
<P>
|
|
Here <A HREF="classes/device.html#synopsis"><CODE>sink</CODE></A> is a convenience base class which provides several
|
|
member types as well as default implementations of several member functions. (The member functions are not needed here.) Given the above definition, we can construct a standard stream buffer as follows:
|
|
</P>
|
|
|
|
<PRE CLASS="broken_ie"> vector<<SPAN CLASS="keyword">char</SPAN>> v;
|
|
streambuf_facade<vector_sink> out(vector_sink(v)); <SPAN CLASS="comment">// stream buffer which appends to v</SPAN></PRE>
|
|
</P>
|
|
|
|
<P>
|
|
We can also define a standard output stream:
|
|
<PRE CLASS="broken_ie"> stream_facade<vector_sink> out(vector_sink(v));</PRE>
|
|
</P>
|
|
|
|
<P>It's tempting to write:</P>
|
|
<PRE CLASS="broken_ie"> vector<<SPAN CLASS="keyword">char</SPAN>> v;
|
|
streambuf_facade<vector_sink> out(v); <SPAN CLASS="comment">// error!!</SPAN></PRE>
|
|
<P>
|
|
Unfortunately, constructor-argument-forwarding only works for constuctors which take their arguments by value or by <CODE>const</CODE> reference; the <CODE>vector_sink</CODE> constructor takes a non-<CODE>const</CODE> reference.<SUP><A CLASS="footnote_ref" NAME="note_1_ref" HREF="#note_1">[1]</A></SUP>
|
|
</P>
|
|
|
|
<P>For easier ways to append to STL sequences, <I>see</I> <A HREF="adapters.html">STL Sequence Adapters</A>.</P>
|
|
|
|
<A NAME="tutorial_input_filter"></A>
|
|
<H4>Filtering Input</H4>
|
|
|
|
<P>
|
|
Suppose we want to filter data read from a standard input stream. Let us say we wish to read only the alphabetic
|
|
characters. We can do this by defining a component — an <A HREF="concepts/input_filter.html">InputFilter</A> — which reads data from a provided Source and modifies it before returning it to the user:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> <SPAN CLASS="preprocessor">#include</SPAN> <SPAN CLASS="literal"><ctype.h></SPAN> <SPAN CLASS="comment">// isalpha</SPAN>
|
|
<SPAN CLASS="preprocessor">#include</SPAN> <SPAN CLASS="literal"><cstdio></SPAN> <SPAN CLASS="comment">// EOF</SPAN>
|
|
<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">// input_filter</SPAN>
|
|
<SPAN CLASS="preprocessor">#include</SPAN> <A CLASS="header" HREF="../../../boost/iostreams/concepts.hpp"><SPAN CLASS="literal"><boost/iostreams/operations.hpp></SPAN></A> <SPAN CLASS="comment">// get</SPAN>
|
|
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> boost::io;
|
|
|
|
<SPAN CLASS="keyword">struct</SPAN> alphabetic_input_filter : <SPAN CLASS="keyword">public</SPAN> input_filter {
|
|
<SPAN CLASS="keyword">template</SPAN><<SPAN CLASS="keyword">typename</SPAN> Source>
|
|
<SPAN CLASS="keyword">int</SPAN> get(Source& src)
|
|
{
|
|
<SPAN CLASS="keyword">int</SPAN> c;
|
|
<SPAN CLASS="keyword">while</SPAN> ((c = boost::iostreams::get(src)) != EOF && !isalpha(c))
|
|
;
|
|
<SPAN CLASS="keyword">return</SPAN> c;
|
|
}
|
|
};</PRE>
|
|
|
|
</P>
|
|
<P>
|
|
Here <A HREF="classes/filter.html#synopsis"><CODE>input_filter</CODE></A> is a convenience base class which provides several member types as well as default implementations of several member functions. (The member functions are not needed here.) The function <A HREF="functions/get.html"><CODE>boost::iostreams::get</CODE></A> reads a character from an arbitrary Source; it is provided by the Iostreams Library to allow all Sources to be accessed with a single interface.<A CLASS="footnote_ref" NAME="note_2_ref" HREF="#note_2"><SUP>[2]</SUP></A> Given the above definition, we can read filtered data from standard input as follows:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> filtering_streambuf<input> in;
|
|
in.push(alphabetic_input_filter());
|
|
in.push(cin);
|
|
</PRE>
|
|
<P>
|
|
The last item added to the chain could be any <A HREF="concepts/source.html">Source</A>. <I>E.g.</I>, we could read a random stream of alpabetic characters using the class <CODE>random_source</CODE> defined above:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> filtering_streambuf<input> in;
|
|
in.push(alphabetic_input_filter());
|
|
in.push(random_source());</PRE>
|
|
<P>
|
|
We could also add any number of <A HREF="concepts/input_filter.html">InputFilters</A> to the chain before adding the <A HREF="concepts/source.html">Source</A>.
|
|
</P>
|
|
<P>
|
|
These examples all remain valid if <CODE>filtering_stream<input></CODE> is substituted everywhere for <CODE>filtering_streambuf<input></CODE>. They could also use the following convenience <CODE>typedef</CODE>s:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> <SPAN CLASS="keyword">typedef</SPAN> filtering_streambuf<input> filtering_istreambuf;
|
|
<SPAN CLASS="keyword">typedef</SPAN> filtering_stream<input> filtering_istream;</PRE>
|
|
|
|
<A NAME="tutorial_output_filter"></A>
|
|
<H4>Filtering Output</H4>
|
|
|
|
<P>
|
|
Suppose we want to filter data written to a standard output stream. Let us say we wish to transform each alphabetic
|
|
character to upper case. We can do this by defining a component — an <A HREF="concepts/output_filter.html">OutputFilter</A> — which modifies data provided by the user before writing it to a given Sink:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> <SPAN CLASS="preprocessor">#include</SPAN> <SPAN CLASS="literal"><ctype.h></SPAN> <SPAN CLASS="comment">// toupper</SPAN>
|
|
<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">// output_filter</SPAN>
|
|
<SPAN CLASS="preprocessor">#include</SPAN> <A CLASS="header" HREF="../../../boost/iostreams/concepts.hpp"><SPAN CLASS="literal"><boost/iostreams/operations.hpp></SPAN></A> <SPAN CLASS="comment">// put</SPAN>
|
|
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> boost::io;
|
|
|
|
<SPAN CLASS="keyword">struct</SPAN> toupper_output_filter : <SPAN CLASS="keyword">public</SPAN> output_filter {
|
|
<SPAN CLASS="keyword">template</SPAN><<SPAN CLASS="keyword">typename</SPAN> Sink>
|
|
<SPAN CLASS="keyword">void</SPAN> put(Sink& snk, <SPAN CLASS="keyword">char</SPAN> c)
|
|
{
|
|
boost::iostreams::put(snk, toupper(c));
|
|
}
|
|
};</PRE>
|
|
<P>
|
|
Here <A HREF="classes/filter.html#synopsis"><CODE>output_filter</CODE></A> is a convenience base class which provides several member types as well as default implementations of several member functions. (The member functions are not needed here.) The function <A HREF="functions/put.html"><CODE>boost::iostreams::put</CODE></A> writes a character to an arbitrary Sink; it is provided by the Iostreams Library to allow all Sinks to be accessed with a single interface.<A CLASS="footnote_ref" NAME="note_3_ref" HREF="#note_3"><SUP>[3]</SUP></A> Given the above definition, we can write filtered data to standard output as follows:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> filtered_streambuf<output> out;
|
|
out.push(toupper_output_filter());
|
|
out.push(cout);</PRE>
|
|
<P>
|
|
The last item added to the chain could be any <A HREF="concepts/sink.html">Sink</A>. <I>E.g.</I>, we could append uppercase characters to a standard <CODE>vector</CODE> using the class <CODE>vector_sink</CODE> defined above:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> vector<<SPAN CLASS="keyword">char</SPAN>> v;
|
|
filtered_streambuf<output> out;
|
|
out.push(toupper_output_filter());
|
|
out.push(vector_sink(v));
|
|
</PRE>
|
|
|
|
<P>
|
|
We could also add any number of <A HREF="concepts/output_filter.html">OutputFilters</A> to the chain before adding the <A HREF="concepts/sink.html">Sink</A>.
|
|
</P>
|
|
<P>
|
|
These examples all remain valid if <CODE>filtering_stream<output></CODE> is substituted everywhere for <CODE>filtering_streambuf<output></CODE>. They could also use the following convenience <CODE>typedef</CODE>s:
|
|
</P>
|
|
<PRE CLASS="broken_ie"> <SPAN CLASS="keyword">typedef</SPAN> filtering_streambuf<output> filtering_ostreambuf;
|
|
<SPAN CLASS="keyword">typedef</SPAN> filtering_stream<output> filtering_ostream;</PRE>
|
|
|
|
<A NAME="tutorial_multi_char_filters"></A>
|
|
<H4>Efficient Filtering: Multi-Character Filters</H4>
|
|
|
|
<P>
|
|
A complex filtering operation will often not be inlinable. In such cases, each time a character is
|
|
read from an ordinary <A HREF="concepts/input_filter.html">InputFilter</A> one incurs the overhead of a call to
|
|
its member function <CODE>get</CODE>. To avoid this overhead, the library allows
|
|
users to define <A HREF="concepts/multi-character.html">Multi-Character</A> Filters which can process several characters
|
|
at once by implementing a member function <CODE>read</CODE>. This function will typically be called only when the buffer associated with the Filter by the Iostreams Library becomes full. For example, the following is a multi-character implementation of the <CODE>alphabetic_input_filter</CODE> described <A HREF="#tutorial_input_filter">above</A>.
|
|
</P>
|
|
|
|
<PRE CLASS="broken_ie"> <SPAN CLASS="preprocessor">#include</SPAN> <SPAN CLASS="literal"><ctype.h></SPAN> <SPAN CLASS="comment">// isalpha</SPAN>
|
|
<SPAN CLASS="preprocessor">#include</SPAN> <SPAN CLASS="literal"><cstdio></SPAN> <SPAN CLASS="comment">// EOF</SPAN>
|
|
<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">// multi_char_input_filter</SPAN>
|
|
<SPAN CLASS="preprocessor">#include</SPAN> <A CLASS="header" HREF="../../../boost/iostreams/concepts.hpp"><SPAN CLASS="literal"><boost/iostreams/operations.hpp></SPAN></A> <SPAN CLASS="comment">// get</SPAN>
|
|
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> boost::io;
|
|
|
|
<SPAN CLASS="keyword">struct</SPAN> alphabetic_input_filter : <SPAN CLASS="keyword">public</SPAN> multi_char_input_filter {
|
|
<SPAN CLASS="keyword">template</SPAN><<SPAN CLASS="keyword">typename</SPAN> Source>
|
|
streamsize read(Source& src, <SPAN CLASS="keyword">char</SPAN>* s, streamsize n)
|
|
{
|
|
<SPAN CLASS="keyword">int</SPAN> c;
|
|
<SPAN CLASS="keyword">char</SPAN>* first = s;
|
|
<SPAN CLASS="keyword">char</SPAN>* last = s + n;
|
|
<SPAN CLASS="keyword">while</SPAN> ( first != last &&
|
|
(c = boost::iostreams::get(src)) != EOF &&
|
|
isalpha(c) )
|
|
{
|
|
*first++ = c;
|
|
}
|
|
<SPAN CLASS="keyword">return</SPAN> <SPAN CLASS="keyword">static_cast</SPAN><streamsize>(first - s);
|
|
}
|
|
};</PRE>
|
|
|
|
<!-- <P>
|
|
When a Filter is added to a filtering stream or stream buffer the instance of <CODE>streambuf_facade</CODE> created to hold it uses a moderately-sized character buffer by default. When a Multi-Character InputFilter is used, its member function <CODE>read</CODE> will generally be called only when this buffer become empty, and the value of <CODE>n</CODE> will generally be equal to the size of the buffer. This can significantly reduce the total number of function calls.
|
|
</P> -->
|
|
|
|
<P>OutputFilters can also be <A HREF="concepts/multi-character.html">Multi-Character</A>. For example:</P>
|
|
<PRE CLASS="broken_ie"> <SPAN CLASS="preprocessor">#include</SPAN> <SPAN CLASS="literal"><ctype.h></SPAN> <SPAN CLASS="comment">// toupper</SPAN>
|
|
<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">// multichar_output_filter</SPAN>
|
|
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> std;
|
|
<SPAN CLASS="keyword">using</SPAN> <SPAN CLASS="keyword">namespace</SPAN> boost::io;
|
|
|
|
<SPAN CLASS="keyword">struct</SPAN> toupper_filter : <SPAN CLASS="keyword">public</SPAN> multichar_output_filter {
|
|
<SPAN CLASS="keyword">template</SPAN><<SPAN CLASS="keyword">typename</SPAN> Sink>
|
|
<SPAN CLASS="keyword">void</SPAN> write(Sink& snk, <SPAN CLASS="keyword">const</SPAN> <SPAN CLASS="keyword">char</SPAN>* s, streamsize n)
|
|
{
|
|
<SPAN CLASS="keyword">while</SPAN> (n-- > <SPAN CLASS="numeric_literal">0</SPAN>)
|
|
boost::iostreams::put(snk, toupper(*s++));
|
|
}
|
|
};</PRE>
|
|
|
|
<!-- Begin Footnotes -->
|
|
|
|
<HR>
|
|
|
|
<P>
|
|
<A CLASS="footnote_ref" NAME="note_1" HREF="#note_1_ref"><SUP>[1]</SUP></A>This is an instance of a limitation of C++ known as the <I>forwarding problem</I> (<I>see</I> <A CLASS="bib_ref" HREF="bibliography.html#dimov">[Dimov]</A>).
|
|
</P>
|
|
|
|
<P>
|
|
<A CLASS="footnote_ref" NAME="note_2" HREF="#note_2_ref"><SUP>[2]</SUP></A>Technically, <CODE>boost::iostreams::get</CODE> and <CODE>boost::iostreams::read</CODE> require that a Source be <A HREF="concepts/direct.html"><I>indirect</I></A>.
|
|
</P>
|
|
|
|
<P>
|
|
<A CLASS="footnote_ref" NAME="note_3" HREF="#note_3_ref"><SUP>[3]</SUP></A>Technically, <CODE>boost::iostreams::put</CODE> requires that a Sink be <A HREF="concepts/direct.html"><I>indirect</I></A>.
|
|
</P>
|
|
|
|
<!-- End Footnotes -->
|
|
|
|
|
|
<!-- 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 Jonathan Turkanis, 2004</P>
|
|
<P CLASS="copyright">
|
|
Use, modification, and distribution are subject to the Boost Software License, Version 1.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> |