I got tired of the boilerplate and tedium restoring formatting context, so I made an RAII stasher that relies on the destroy-at-end-of-full-statement temporary semantics. With C++17 I can get it down to a single type, no helpers:
#include <iostream>
template< template<typename, class> class streamtmp, typename charT, class traits >
struct fmtstash {
typedef streamtmp<charT,traits> streamtype;
streamtype &from;
std::ios_base::fmtflags flags;
std::streamsize width;
std::streamsize precision;
charT fill;
fmtstash(streamtype &str)
: from(str), flags(str.flags()), width(str.width())
, precision(str.precision()), fill(str.fill()) {}
~fmtstash() { from.flags(flags), from.width(width), from.precision(precision), from.fill(fill); }
template<typename T> streamtype &operator<<(const T &rhs) { return from << rhs; }
template<typename T> streamtype &operator>>( T &rhs) { return from >> rhs; }
};
int main(int c, char **v)
{
using namespace std;
fmtstash(cout) << hex << 51901 <<'\n';
cout << 51901 <<'\n';
return 0;
}
which produces about the easiest-to-write-and-read formatting I can manage.
Am I missing any bets here? Shorter or more robust is what I'm after.
The biggest improvement I found since I first wrote it is to use from << rhs
instead of the from.operator<<(rhs)
I had, since user-defined stream insert/extractors have to be free functions and of course an explicit member callout breaks those.
I could make the template args simpler, just template<class streamtmp>
, the full breakout is a holdover from when I had the internal reference as ios_base<charT,traits> &from;
instead of the full original type, but I ran across a broken inserter that wanted to be first or only and took an ostream reference as its left operand.
And of course the boost savers don't supply the inserter and extractor templates at all. Is this an oversight or is there some argument against using the destroy-at-end-of-full-expression semantics this way?