The iostreams support adding custom data to them, so you don't need to write a full derived class just to add an indentation level that will be operated on by manipulators. This is a little-known feature of iostreams, but comes in handy here.
You would write your manipulators like this:
/* Helper function to get a storage index in a stream */
int get_indent_index() {
/* ios_base::xalloc allocates indices for custom-storage locations. These indices are valid for all streams */
static int index = ios_base::xalloc();
return index;
}
ios_base& inc_ind(ios_base& stream) {
/* The iword(index) function gives a reference to the index-th custom storage location as a integer */
stream.iword(get_indent_index())++;
return stream;
}
ios_base& dec_ind(ios_base& stream) {
/* The iword(index) function gives a reference to the index-th custom storage location as a integer */
stream.iword(get_indent_index())--;
return stream;
}
template<class charT, class traits>
basic_ostream<charT, traits>& endl_ind(basic_ostream<charT, traits>& stream) {
int indent = stream.iword(get_indent_index());
stream.put(stream.widen('\n');
while (indent) {
stream.put(stream.widen('\t');
indent--;
}
stream.flush();
return stream;
}