
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?
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. 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). 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? 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. 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.