Suppose you want to write a filter which wraps lines of text to ensure that no line exceeds a certain maximum length. For simplicity, let's not bother to wrap lines at word boundaries or to insert hyphens. The basic algorithm is as follows: You examine characters one at a time, fowarding them as-is, keeping track of the current column number. When you encounter a newline character, you forward it and reset the column count. When the column count reaches the maxim value, you insert a newline character before the current character and reset the column count.
In the next three sections, I'll express this algorithm as a stdio_filter
, an InputFilter and an OutputFilter. The source code can be found in the header <libs/iostreams/example/line_wrapping_filter.hpp>
. These examples were inspired by James Kanze's LineWrappingInserter.hh
(see [Kanze]).
line_wrapping_stdio_filter
You can express a line-wrapping Filter as a stdio_filter
as follows:
#include <cstdio> // EOF #include <iostream> // cin, cout #include <boost/iostreams/filter/stdio.hpp> namespace boost { namespace iostreams { namespace example { class line_wrapping_stdio_filter : public stdio_filter { public: explicit line_wrapping_stdio_filter(int line_length = 80) : line_length_(line_length), col_no_(0) { } private: void do_filter(); void do_close(); void put_char(int c); int line_length_; int col_no_; }; } } } // End namespace boost::iostreams:example
Let's look first at the definition of the helper function put_char
:
void put_char(int c) { std::cout.put(c); if (c != '\n') ++col_no_; else col_no_ = 0; }
This function writes the given character to std::cout
and increments the column number, unless the character is a newline, in which case the column number is reset. Using put_char
, you can implement the virtual
function do_filter
as follows:
void do_filter() { int c; while ((c = std::cin.get()) != EOF) { if (c != '\n' && col_no_ >= line_length_) put_char('\n'); put_char(c); } }
The while
loop simply reads a character from std::cin
and writes it to std::cout
, inserting an extra newline character as needed to prevent the column count from exceeding line_length_
.
Finally, the member function do_close
overrides a private
virtual
function declared in stdio_filter
.
void do_close() { col_no_ = 0; }
Its purpose is to reset the state of the Filter when a stream is closed.
line_wrapping_input_filter
You can express a line-wrapping Filter as an InputFilter as follows:
#include <boost/iostreams/char_traits.hpp> // EOF, WOULD_BLOCK #include <boost/iostreams/concepts.hpp> // input_filter #include <boost/iostreams/operations.hpp> // get namespace boost { namespace iostreams { namespace example { class line_wrapping_input_filter : public input_filter { public: explicit line_wrapping_input_filter(int line_length = 80) : line_length_(line_length), col_no_(0), has_next_(false) { } template<typename Source> int get(Source& src); template<typename Sink> void close(Sink&); private: int get_char(int c); int line_length_; int col_no_; int next_; int has_next_; }; } } } // End namespace boost::iostreams:example
Let's look first at the helper function get_char
:
int get_char(int c) { if (c != '\n') ++col_no_; else col_no_ = 0; return c; }
This function updates the column count based on the given character c
, then returns c
. Using get_char
, you can implement get
as follows:
template<typename Source> int get(Source& src) { if (has_next_) { has_next_ = false; return get_char(next_); } int c; if ((c = iostreams::get(src)) == EOF || c == WOULD_BLOCK) return c; if (c != '\n' && col_no_ >= line_length_) { next_ = c; has_next_ = true; return get_char('\n'); } return get_char(c); }
An InputFilter which is not a MultiCharacterFilter can only return a single character at a time. Consequently, if you wish to insert a newline before a character c
read from src
, you must store c
and return it the next time get
is called. The member variable next_
is used to store such a character; the member variable has_next_
keeps track of whether such a character is stored.
The implementation of get
first checks to see if there is stored character, and returns it if there is. Otherwise, it attemps to read a character from src
. If no character can be read, it returns one of the special values EOF
or WOULD_BLOCK
. Otherwise, it checks whether a newline must be inserted. If so, it stores the current character and returns a newline. Otherwise, it returns the current character.
Finally, the member function close
resets the Filter's state:
template<typename Sink> void close(Sink&) { col_no_ = 0; has_next_ = false; }
line_wrapping_output_filter
You can express a line-wrapping Filter as an OutputFilter as follows:
#include <boost/iostreams/concepts.hpp> // output_filter #include <boost/iostreams/operations.hpp> // put namespace boost { namespace iostreams { namespace example { class line_wrapping_output_filter : public output_filter { public: explicit line_wrapping_output_filter(int line_length = 80) : line_length_(line_length), col_no_(0) { } template<typename Sink> bool put(Sink& dest, int c); template<typename Sink> void close(Sink&); private: template<typename Sink> bool put_char(Sink& dest, int c); int line_length_; int col_no_; }; } } } // End namespace boost::iostreams:example
Let's look first at the helper function put_char
:
template<typename Sink> bool put_char(Sink& dest, int c) { if (!iostreams::put(dest, c)) return false; if (c != '\n') ++col_no_; else col_no_ = 0; return true; }
This function attempts to write the character c
to the given Sink and updates the column count if successful. Using put_char
, you can implement put
as follows:
template<typename Sink> bool put(Sink& dest, int c) { if (c != '\n' && col_no_ >= line_length_ && !put_char(dest, '\n')) return false; return put_char(dest, c); }
This function first checks the given character and the column count to see whether a newline character must be inserted. If so, it attempts to write a newline using put_char
and returns false if the operation fails. Otherwise, it attempts to write the the given character using put_char
. Note that if a newline is successfully inserted but the attempt to write the given character fails, the column count will be updated to reflect the newline character so that the next attempt to write the given character will not cause a newline to be inserted.
Finally, the member function close
resets the Filter's state:
template<typename Sink> void close(Sink&) { col_no_ = 0; }
© 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)