All the Filters you've seen so far — except for those that derive from stdio_filter
— process characters one at a time. If you instead process several characters at once, you can often reduce the number of function calls it takes to filter a character sequence, resulting in more efficient code. This is what Multi-Character Filters allow you to do.
A typical narrow-character Multi-Character InputFilter looks like this:
#include <iosfwd> // streamsize #include <boost/iostreams/categories.hpp> // tags class my_input_filter { public: typedef char char_type; struct category : input_filter_tag, multichar_tag { }; template<typename Source> std::streamsize read(Source& src, char* s, std::streamsize n) { // Read up to n filtered characters into the buffer s, // returning the number of characters read or -1 for EOF. // Use src to access the unfiltered character sequence } /* Other members */ };
Notice that the member type category is a struct
convertible to input_filter_tag
and to multichar_tag
. This tells the Iostream library that my_input_filter
is a Multi-Character Filter and an InputFilter. You could have achieved the same effect several ways. E.g.,
struct category : input, filter_tag, multichar_tag { }; /* or */ typedef multichar_input_filter_tag category;
(For details, see Mode Tags and Category Tags.)
You could also write the above example as follows:
#include <iosfwd> // streamsize #include <boost/iostreams/concepts.hpp> // multichar_input_filter class my_input_filter : public multichar_input_filter { public: template<typename Source> std::streamsize read(char* s, std::streamsize n); /* Other members */ };
Here multichar_input_filter
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
.
shell_comments_multichar_input_filter
You can express a shell comments Filter as an Multi-Character InputFilter as follows:
#include <boost/iostreams/char_traits.hpp> // EOF, WOULD_BLOCK #include <boost/iostreams/concepts.hpp> // multichar_input_filter #include <boost/iostreams/operations.hpp> // get namespace boost { namespace iostreams { namespace example { class shell_comments_multichar_input_filter : public multichar_input_filter { public: explicit shell_comments_multichar_input_filter(char comment_char = '#') : comment_char_(comment_char), skip_(false) { } template<typename Source> std::streamsize read(Source& src, char* s, std::streamsize n) { for (std::streamsize z = 0; z < n; ++z) { int c; while (true) { if ((c = boost::iostreams::get(src)) == EOF) return z != 0 ? z : -1; else if (c == WOULD_BLOCK) return z; skip_ = c == comment_char_ ? true : c == '\n' ? false : skip_; if (!skip_) break; } s[z] = c; } return n; } template<typename Source> void close(Source&) { skip_ = false; } private: char comment_char_; bool skip_; }; } } } // End namespace boost::iostreams:example
Note that the implementation of read
is very similar to what you would get if you put the implementation of shell_comments_input_filter::get
inside a for
loop iterating from 0
to n
. InputFilters which call themselves recursively, such as tab_expanding_input_filter
, are much harder to transform into Multi-Character filters.
A typical narrow-character Multi-Character OutputFilter looks like this:
#include <iosfwd> // streamsize #include <boost/iostreams/categories.hpp> // tags class my_output_filter { public: typedef char char_type; struct category : output_filter_tag, multichar_tag { }; template<typename Sink> std::streamsize write(Sink& dest, const char* s, std::streamsize n) { // Consume up to n filtered characters from the buffer s, // writing filtered characters to dest. Return the number // of characters consumed. } /* Other members */ };
Notice that the member type category is a struct
convertible to keyword
and to multichar_tag
. This tells the Iostream library that my_output_filter
is a Multi-Character Filter and an OutputFilter. As with Multi-Character InputFilters, you could have achieved the same effect several different ways. E.g.,
struct category : output, filter_tag, multichar_tag { }; /* or */ typedef multichar_output_filter_tag category;
(For details, see Mode Tags and Category Tags.)
You could also write the above example as follows:
#include <iosfwd> // streamsize #include <boost/iostreams/concepts.hpp> // multichar_output_filter class my_output_filter : public multichar_output_filter { public: template<typename Sink> std::streamsize write(Sink& dest, const char* s, std::streamsize n); /* Other members */ };
Here multichar_output_filter
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
.
shell_comments_multichar_output_filter
You can express a shell comments Filter as an Multi-Character OutputFilter as follows:
#include <boost/iostreams/char_traits.hpp> // EOF, WOULD_BLOCK #include <boost/iostreams/concepts.hpp> // multichar_output_filter #include <boost/iostreams/operations.hpp> // get namespace boost { namespace iostreams { namespace example { class shell_comments_multichar_output_filter : public multichar_output_filter { public: explicit shell_comments_multichar_output_filter(char comment_char = '#') : comment_char_(comment_char), skip_(false) { } template<typename Sink> std::streamsize write(Sink& dest, const char* s, std::streamsize n) { std::streamsize z; for (z = 0; z < n; ++z) { int c = s[z]; skip_ = c == comment_char_ ? true : c == '\n' ? false : skip_; if (skip_) continue; if (!iostreams::put(dest, c)) break; } return z; } template<typename Source> void close(Source&) { skip_ = false; } private: char comment_char_; bool skip_; }; } } } // End namespace boost::iostreams:example
Note that the implementation of write
is very similar to what you would get if you put the implementation of shell_comments_output_filter::put
inside a for
loop iterating from 0
to n
. OutputFilters which call themselves recursively, such as unix2dos_output_filter
, are much harder to transform into Multi-Character filters.
© 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)