Boost logo

Boost :

From: Jonathan Turkanis (technews_at_[hidden])
Date: 2004-09-03 16:06:39


"Rob Stewart" <stewart_at_[hidden]> wrote in message
news:200409031905.i83J5JU21799_at_lawrencewelk.systems.susq.com...
> From: "Jonathan Turkanis" <technews_at_[hidden]>
> > "Thorsten Ottosen" <nesotto_at_[hidden]> wrote in message

> > One of the original motivations for introducing the i/o categories (they
turned
> > out to be useful for a lot of other things) was to avoid having separate
> > functions push_filter and push_resource. I consider it a major
simplification of
> > the interface.
>
> I agree with Thorsten that some means of ensuring that parts
> aren't assembled in the wrong order would be helpful. Whether
> that means separate functions, or detection of the type of object
> being pushed, it seems like preventing misuse should be a bigger
> priority than "a major simplification of the interface."

I'm not sure I follow. You already get a runtime error if you try to add a
filter or resource to a chain that is already complete. This is mention in the
specification for push (see http://tinyurl.com/49j6u) E.g.,

    filtering_ostream out;
    out.push(zlib_compressor());
    out.push(file_sink("hello.z"));
    out.push(base64_encoder()); // error !!
    out.push(tcp_sink(www.microsoft.com, 80)); // error !!

Isn't this enough? (Maybe it should be an assertion failure instead of an
exception.)

Perhaps you would like a compile-time error instead. Note that having separate
functions for pushing filters and resources would not help in that case. To
generate a compile-time error would require that the types of all the filters
and resources be encoded into the type of the filtering stream. This was
suggested last year by Larry Evans and recently by Robert Ramey (if I understood
hime correctly.)

The problems are:

- much more complex interface
- less flexible a runtime
- neglible gain in efficiency, since most filtering operations aren't inlineable

Finally, it's already the programmer's responsibility to ensure that the filters
are added in the right order -- no amount of template magic will guarantee
this -- so making sure to add the resource at the end is not much of an extra
burden.

> There are plenty of places where one can misuse existing
> libraries, including the Standard Library, so perhaps requiring
> that protection from this library is misguided. So, here's
> another approach: perhaps you could create a set of overloaded
> make_* functions that take a varying number of filter arguments
> followed by an optional (via overloading) resource argument.
> Then, those functions can ensure that if there is a resource, it
> is push()'d last.

Yes, that would work. There are two versions I can think of:

1. Orginally I had a function link(...) which created an inline chain of filters
and resources, but I eliminated it to make the library smaller. It didn't occur
to me to add a compile-time check that the last element was a resource; in fact,
I thought it would also be useful for chaining filters alone. However, this
might be a good reason to restore the function link, with the added check.

2. Dietmar Kuehl mentioned a piping syntax originall proposed by JC van Winkel.
E.g.,

    filtering_stream
        out( base64_encoder() | zlib_compressor() | file_sink("file") );

This syntax, too, could be modified to do a compile-time check that the last
item in the chain is a resource.

I like both of these ideas as a syntactic convenience but not as a way to
enforce at compile time that resources are added last. For this enforcement to
have teeth, it would be necessary to remove the stack interface, which would be
considered 'unsafe'. But the stack interface is natural and convenient, and
essential for some purposes.

For example, a natural way to implement a compression stream would be to derive
from a filtering stream and push a compression filter onto the stack in the
stream's constructor. Templated open and close functions would then be
implemented by pushing and popping resources:

    struct zlib_ostream : filtering_ostream {
        zlib_ostream() { push(zlib_compressor()); }

        template<typename Source>
        zlib_ostream(const Source& src )
       {
            push(zlib_compressor());
            open(src);
        }

        template<typename Source>
        void open(const Source& src )
       {
            BOOST_STATIC_ASSERT(is_resource<src>::value);
            push(src);
        }

       bool is_open() const { return is_complete(); }

        template<typename Source>
        void close(const Source& src )
       {
            assert(is_open());
            pop();
       }
    };

I sort of feel like I'm beating a dead horse :-) Is a runtime error (or
assertion failure) sufficient, or do you feel strongly that there needs to be a
compile-time check?

Jonathan


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