
"Jonathan Graehl" <jonathan@graehl.org> wrote in message news:413F7C5A.1030701@graehl.org...
struct alphabetic_input_filter : public input_filter { template<typename Source> int get(Source& src) { character c; while ((c = boost::io::get(src)).good() && !isalpha(c)) ; return c; } };
(boost::io::)character is just a type that wraps an int with a good() test?
int_type or optional<char> would be good enough for that. The important point is that there are two separate 'not good' states -- one means end-of-sequence and the other means no data is currently available.
Here, eof and fail values are passed on to the caller unchanged. If you want to send an eof or fail notification explicitly, you'd write return eof() or return fail().
Now the big question: is the above formulation too convoluted to teach to an average user who is interested only in plain, blocking i/o?
That seems fine. For such a user, this is just boilerplate code pasted from documentation examples.
What worries me is that users already know about char, int and EOF. To use character properly probably requires more than just copying from examples.
If you really care to make filters written by naive users via a simple blocking interface applicable to more advanced nonblocking scenarios, you can design a generic adapter that turns a blocking filter into a nonblocking one. That is, it would wrap both the upstream and downstream, I imagine with a dynamically growing buffer that will accept any single output the naive user wants to produce, but then flagging when its downstream consumer doesn't accept the entire amount, and returning the EAGAIN equivalent to its upstream instead of calling the user's naive blocking method. This strategy would require a close notification since the buffered unaccepted stuff would be left alone until the next write attempt (unless you want to spawn a thread to spin attempting to empty the buffer).
I hadn't thought of that. I'd much rather teach people how to write correct filters, so they can be used with maximum efficiency. There's also no good way to turn a blocking input filter into a non-blocking one -- unless you consider that every blocking input filter is sort of a degenerate non-blocking filter.
About the close() or open() methods for a filter that wants to write some prelude or coda (e.g. gzip): aren't these only necessary because you can't guarantee that the constructors and destructors for the filter stack are called in the proper order?
No -- filters should be reusable. Here's an example from a reply I wrote to Rob Stewart (it turned out not to be relevant to that discussion, but maybe it'll be relevant here ;-). "Jonathan Turkanis" <technews@kangaroologic.com> wrote in message news:chalsi$cmb$1@sea.gmane.org...
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(); } };
int main() { using namespace boost::io; zlib_ostream out; out.open(file_sink("hello_world")); out << "hello world!"; out.close(); out.open(file_sink("goodbye_world")); out << "goodbye world!"; } Only one zlib_compressor is constructed, but it is used several times.
It would be nice if source -> A -> B -> sink could guarantee that B is (finally) constructed after sink, and then A is constructed after having been linked to B. That is, aren't the filters passed by const reference and only copy constructed once into the filter stack? I guess the part about ensuring that A is linked to B before the user constructor code might be accomplished by inheritance (superclass constructors always complete before subclass constructor executes, and the reverse for destructors?) I'm not sure if this is too clever, or can't be made to work portably, though.
Actually, I can run the destructors in any order I want, since I'm using boost::optional<Filter> to avoid requiring that filters and resources be default constructible. So I can just do filter_ = none;
I don't think a second, simpler interface would be that much of a win; the complexity of having two interfaces or types of filters would add as much confusion as it simplifies the blocking case.
I've lost you here. Which is the 'second, simpler interface' which you don't think is a good idea? Best Regards, Jonathan