Boost logo

Boost :

From: Jonathan Turkanis (technews_at_[hidden])
Date: 2005-01-09 02:02:25


christopher diggins wrote:
> Thank you very much to everyone who has been helping me out, and have
> made some suggestions. It took me a bit to realize the implications
> of what some people were suggesting, so I apologize if I am a bit
> slow to catch up.
>
> I now have code which allows two arbitrary functions ( which have no
> parameters and return void ) to be chained together from cout of one
> to the cin of the other:

Hi Christopher,

I don't have much time to write today but I'd like to make several observations
about this discussion.

It seems to me that what you are trying to achieve is something that is already
well-supported by the iostreams library. The iostreams library offers users a
number of different ways to write filters, with the understanding that some
methods will be more efficient or convenient for a particular purpose than
another.

----
The basic types of filters are these:
  - push filter (aka output filter): given a sequence of characters and a model
of Sink, the filter writes filtered characters to the Sink using the generic
output functions put() and write().
  - pull filter (aka input filter): Given a model of Source, the filter returns
a specified number of characters from the filtered sequence.
Two important criteria in designing the filter and device concepts were these:
- They should not be hard-wired to deal with a particular type of upstream or
downstream object; e.g., they should not be forced to deal with standard input
and output streams. This is important for flexibility and efficiency.
- They should be able to filter small subsequences from the middle of an input
sequence, rather than processing entire streams at once. This is important for
memory usage and because some input sequences, such as continuous stock-tickers,
have no natural end.
On top of these basic filters can be built filters with a more user-friendly
interface or filters which are suited to some specific purpose. For instance,
- symmetric filter (useful for wrapping C filtering APIs such as zlib): given
two character arrays, one for input and one for output, the filter consumes some
characters from the input array and writes some characters from the filtered
sequence to the output array.
- one_step_filter: given an entire input sequence as a std::vector, the filter
appends the entire filtered sequence to a second std::vector.
----
one_step_filters are useful when memory usage is not an issue and when streams
have a well-defined beginning and end. Using one_step_filters, it is simple to
define filters which take an input stream and an output stream, read input from
the input stream and write filtered output to the output stream:
   #include <boost/iostreams/device/back_inserter.hpp>
   #include <boost/iostreams/filter/one_step_filter.hpp>
   #include <boost/iostreams/stream_facade.hpp>
   template<typename Ch>
   class co_filter : public one_step_filter<Ch> {
      typedef std::vector<Ch> vector_type;
      virtual void do_filter(
             std::basic_istream<Ch>& in,
             std::basic_ostream<Ch>& out ) = 0;
      // declared in one_step_filter:
      virtual void do_filter(const vector_type& src, vector_type& dest)
      {
           // Input stream which reads from src:
           stream_facade< basic_array_source<Ch> >
              in( &src[0], &src[0] + src.size() );
          // Output stream which appends to dest
         stream_facade< boost::io::back_insert_device<vector_type> >
             out(boost::io::back_inserter(dest));
         do_filter(in, out);
      }
   };
Given the above definition, if you write a class which derives from co_filter
and override the pure virtual function do_filter, you can add it to the
filtering streams from the iostreams library and it will work as you have
described, if I understand you correctly.
----
Regarding the pipe notation, currently it can only be used as follows:
   filtering_istream in(filter1() | filter2() | filter3() | source());
or
  filtering_ostream out(filter1() | filter2() | filter3() | sink());
However, I should be able to extend it so that if a chain contains both a source
and a sink, boost::io::copy is invoked. E.g.,
  source() | filter1() | filter2() | filter3() | sink()
would be equivalent to
 filtering_ostream out(filter1() | filter2() | filter3() | sink());
 boost::io::copy(source, out);
If I can make this work, and there are no objections, I'll add it.
----
> 1) create wrapper objects around the filters, this would allow the
> passing of data to functions, like a command line string
Filters can already be passed arbitrary data.
> 2) chain arbitrarily long sequences by creating a pipeline object,
> and doing the piping in its destructor
Arbitrarily long sequences can already be chained.
> 3) allow the chaining of streams
I'm not sure what this means, but it can probably already be done ;-)
> 4) allow the chaining of the various iostreams concepts
See above.
> 5) allow the chaining of FILE* (i.e. popen, etc.)
> 6) allow the chaining of processes
This would be a good addition to the library, in the form of the system_filter I
described here: http://tinyurl.com/53w9o
> 7) allow threading of functions
Do you mean this: support filters which think they are processing an entire
stream at once, but really their threads are waiting on some syncronization
object whenever there is no more input available, or output buffers are full?
Jonathan

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk