
"Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:chalsi$cmb$1@sea.gmane.org...
"Rob Stewart" <stewart@sig.com> wrote in message news:200409031905.i83J5JU21799@lawrencewelk.systems.susq.com...
From: "Jonathan Turkanis" <technews@kangaroologic.com>
"Thorsten Ottosen" <nesotto@cs.auc.dk> 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
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost