Boost logo

Boost :

From: Jonathan Turkanis (technews_at_[hidden])
Date: 2004-09-08 14:32:24


"Pavel Vozenilek" <pavel_vozenilek_at_[hidden]> wrote in message
news:chl9nu$sh9$1_at_sea.gmane.org...
>
> "Jonathan Turkanis" wrote:

Pavel, I clearly misunderstood some of your suggesstion (sorry). Before
getting back into the details, let me make some general comments:

I'm hoping to stick, as far as possible, to concepts which can be given correct
default implementations for filters and resources which don't model them.
Otherwise, a concept will be useful only when all the components in a chain of
filter and resources model it, so either

  - it won't be very useful, since lots of common components won't model it, OR
  - users will in effect be required to make their components model the
additional concepts, which interferes with my goal of making it extremely easy
to write simple filters and resources.

Therefore, to the extent possible, I'd like to discover the bare minimum
requirements on a filter or resource which would allow reasonable default
behaviors to be defined for the proposed concepts.

Here's a suggestion for a concept which might be reusable in this way (view with
fixed-width font):

Concept: Resettable

Valid Expressions | Return Type | Semantics
--------------------------------------------------------------------
t.reset(discard) | bool | resets t to the state it had
                      | | upon construction, discarding
                      | | cached data if discard is true.
                      | | Returns true for success.

Perhaps reset(false) could be a resonable default implementation for open and/or
close for many simple filters such as the newline filter, uncommenting filter,
etc

Here's a problem, though: Implementing open and/or close will still be common
enough that I don't want library users to have to specify the categories tags
openable_tag and closable_tag explicitly. So when I define the convenience base
classes, I want to make the i/o category refine openable, closable and
resettable, and provide default no-op implementations. Let'd say the convenience
base class input_filter looks like this:

    struct input_filter {
        typedef char char_type;
        struct category
            : input_filtering_tag,
              openable_tag,
              closable_tag,
              localizable_tag
           { };

        template<typename Source>
        void open(Source&) { }

        template<typename Source>
        void close(Source&) { }

        void reset(bool discard) { }

        ...
    };

Now, if the user only implements reset, I'd like boost::io::open and
boost::io::close to call reset. But if the user implements open, boost::io::open
should call open. Maybe this could be done by comparing the member pointers
&my_input_filter::close<Dummy> and &input_filter::close<Dummy>, but

1. I don't know if this is portable
2. It creates wasteful instantiations.

The other solution would be to use CRTP:

    template<typename Derived>
    struct input_filter {
        typedef char char_type;
        struct category
            : input_filtering_tag,
              openable_tag,
              closable_tag,
              localizable_tag
           { };

        template<typename Source>
        void open(Source&)
        {
             static_cast<Derived&>(*this).reset(false);
        }

        template<typename Source>
        void close(Source&)
        {
             static_cast<Derived&>(*this).reset(false);
        }

        void reset(bool discard) { }
    };

But this make using the convenience base classes more complex:

    struct my_input_filter
        : input_filter<my_input_filter>
    {
        ...
    };

> > > - reset stream settings, either for individual stream or all of them
> > > at once. I have no clue how generic solution may look like
> >
> > You can reset the state of a filter by calling close.
> >
> Maybe. Maybe one may like just to modify some minor property
> of pipeline w/o affecting its main functionality.

The Resettable concept, above, is more along these lines.

> > > - discard any cached data
> > >
> > > E.g. streams are used to send data to radio link.
> > > If problem in airspace is detected, current transmission
> > > is stopped, unsent data thrown aways, jamming sequence
> > > tramsitted and then normal transmission will continue later.

We could use reset(true) for this case.

> >
> > This would be almost easy, using flushable, since you could flush all the
> > filters and connect the last one temporarily to a null_sink. Unfortunately,
> > you'd need a special function to tell the resource to discard its data.
> >
> I see two problems:
>
> - the flushing may take lot of time, if some complex filters are involved,
> discarding may be required quickly-handle-error technique

Right.

> - it is not really sure flush would flush: for example filter removing
> certain word may wait intil words get finished. What is flush()
> semantic of theirs?

This is an important point. When I originally implemented Flushable, flush() was
just a 'suggestion' to flush buffers. So users could never be sure there wasn't
any cached data remaining. I did it this way because for many filters, as you
mentioned, you can't force a complete flush without violating data integrity.
But this whimpy version of flush turned out not to be very useful, which is
partly why I eliminated it.

If I restore the concept, I will probably have flush return true only if all
cached data could be safely sent downstream. This means that sometimes flushing
a filtering_ostream will fail, and in those cases you won't be able to insert or
remove filters while i/o is in progress.

Many text oriented filters will probably be line-preserving, in the sense that a
single line of text is translated to one or more complete lines. Such filters
will often be flushable as long as the end of a line has been reached, so the
end user will know that it's okay to flush the stream after writing a line and
then to insert or remove filters.

> > > - halt temporarily/restart (for output streams only?).
> > > This action may be propagated downstream since some
> > > filters may have busy sub-threads of their own
> >
> > I'm not sure I understand.
> >
> Equivalent to putting null sink on the top of pipeline
> (and maybe after each part of pipeline, if the part acts
> asynchonously).
>
> Not sure now it it is worth the troubles.

Neither am I ;-). Maybe if you can think of an important use case ...

> > > - reset stream settings, either for individual stream or all of them
> > > at once. I have no clue how generic solution may look like
> >
> > This sounds like discard + open.
> >
> Not really, rather like sending SIGHUP to a Unix process.
> No data loss, no open/close.

Hmmm. I guess I quoted the same part of you message twice. Anyway, this would be
reset(false).

> > > - generic command/event interface for stream specific actions.
> > > E.g. using named commands/events.
> >
> > This might be a better approach than making the Concept interfaces fatter
> > and fatter.
> >
> Functions like flush() etc have exact interface, well-defined names
> and semantic. The event/command would be likely typeless.
> I think generic functionality should be explicitly exposed and the rest
> could be handled by commands.

I agree.

> > > - generate string description of what is in stream chain
> > > (like stream::dump() for debugging)
> >
> > The character sequences, or the filter/resource sequence?
> >
> The latter. E.g. for to print debug into on console.

This is a good idea for a debug-mode feature, but I think it only really makes
sense if I implement flushable and allow filters to be swapped in and out during
i/o. Otherwise, it should be pretty obvious which filters are in a chain just by
looking the sequence of pushes.

> > > There could be also example of how to handle 'buffer overflow'
> > > errors e.g. when writing into full socket or time series generated
> > > too fast to be read.
> >
> > Could you elaborate?
> >
> Example which throws (and exception gets caught and handled),
> example which sets failbit or whatever and how it could be handled.

I see. I think I should put some examples in the user's guide under
'Exceptions'.

>
> /Pavel
>

Jonathan


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