# Tutorial

## 2.2.4. Line-Wrapping Filters

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)