// This should be a better example of method overloading. // Here we have a simple anonymous concept of an “output stream”, // which is an object with a void say(const char *, int) method, and // five implementations of it: // - fd_output_stream, on a raw file descriptor; // - buffered_output_stream, providing buffering atop another output // stream; // - rot13_output_stream, rot13ing the text sent to another output // stream; // - vecstream, storing the written text into a vector. // - teestream, copying the written text onto two separate output // streams. #include // for write() #include // for errno #include // for fprintf() in the top exception handler #include // for memcpy() and strerror() #include // for min() and copy() #include // for back_inserter() #include using std::vector; using std::min; using std::copy; using std::back_inserter; class output_error { public: const char *const msg; output_error(const char *msg) : msg(msg) { } }; class fd_output_stream { const int fd; public: fd_output_stream(int fd) : fd(fd) { } void say(const char *s, int n) { if (n != write(fd, s, n)) { throw output_error(strerror(errno)); } } }; class noncopyable { protected: noncopyable() { } ~noncopyable() { } private: noncopyable(const noncopyable &); noncopyable & operator=(const noncopyable &); }; template class buffered_output_stream : noncopyable { wrapped &out; char *const buf; static const int bufsiz = 4096; int bufptr; public: buffered_output_stream(wrapped &out) : out(out), buf(new char[bufsiz]), bufptr(0) { } ~buffered_output_stream() { flush(); delete[] buf; } void say(const char *s, int n) { int i = 0; while (i < n) { int chunk_size = min(n - i, bufsiz - bufptr); memcpy(buf+bufptr, s+i, chunk_size); bufptr += chunk_size; i += chunk_size; if (bufptr == bufsiz) flush(); } } void flush() { out.say(buf, bufptr); bufptr = 0; } }; template class rot13_output_stream { wrapped &out; public: rot13_output_stream(wrapped &out) : out(out) { } void say(const char *s, int n) const { for (int i = 0; i < n; i++) { char c = s[i]; if ( ('A' <= c && c < 'N') || ('a' <= c && c < 'n')) { c += 13; } else if (('N' <= c && c <= 'Z') || ('n' <= c && c <= 'z')) { c -= 13; } // I’m not sure how to invoke the say() functions below here, // either. out.say(&c, 1); } } }; class vecstream { public: vector buf; void say(const char *s, int n) { copy(s, s+n, std::back_inserter(buf)); } }; template class teestream { wrapped1& s1; wrapped2& s2; public: teestream(wrapped1& s1, wrapped2& s2) : s1(s1), s2(s2) { } void say(const char *s, int n) const { s1.say(s, n); s2.say(s, n); } }; template teestream tee(wrapped1 &s1, wrapped2 &s2) { return teestream(s1, s2); } template rot13_output_stream rot13(const wrapped &out) { // This `const_cast` is cheating in order to allow the chaining of // function calls (because anonymous temporary values are implicitly // const; you can’t modify them). I don’t understand how to get rid // of it without using C++11 modifiable rvalues. return rot13_output_stream(const_cast(out)); } // Note that these `say` functions are *not* methods, so they do *not* // overload (or override) the `say` methods of any class. They are // overridden on argument type. template void say(stream &out, const char *s) { out.say(s, strlen(s)); } template void say(stream &out, char c) { out.say(&c, 1); } // I don’t fully understand why I need two copies of the hello() // template here, one for const streams and one for non-const streams. // Maybe because not all my say() methods are const? template void hello(stream &out) { say(out, "hello, "); say(out, "world"); say(out, '\n'); } template void hello(const stream &out) { say(out, "hello, "); say(out, "world"); say(out, '\n'); } int main(int argc, char **argv) { try { fd_output_stream out(1); // If you strace it, you can see it emitting this output in three // separate system calls. hello(out); // But what comes afterwards is all buffered. buffered_output_stream bout(out); hello(bout); vecstream vs; // This sends one copy of the greeting to bout, and the other copy to vs. hello(rot13(tee(bout, vs))); // Now we copy the vs to bout too. say(bout, "-> "); for (vector::iterator it = vs.buf.begin(); it != vs.buf.end(); ++it) { say(bout, *it); } hello(rot13(rot13(bout))); return 0; // At this point, the destructor of bout is implicitly called, // flushing it. } catch (output_error &e) { fprintf(stderr, "output_error: %s\n", e.msg); return 1; } }