We are going to implement a class, format_guard, which can automatically revert any format setting. Additionally, we add a wrapper type, which can contain any value, but when it is printed, it gets special formatting without burdening us with I/O manipulator noise:
- First, we include some headers and declare that we use the std namespace:
#include <iostream>
#include <iomanip>
using namespace std;
- The helper class that tidies up our stream formatting states for us is called format_guard. Its constructor saves the formatting flags, which std::cout has set at the moment. Its destructor restores them to the state it had when the constructor was called. This effectively revokes any formatting settings that were applied in between:
class format_guard {
decltype(cout.flags()) f {cout.flags()};
public:
~format_guard() { cout.flags(f); }
};
- Another little helper class is scientific_type. Because it's a class template, it can wrap any payload type as a member variable. It basically does nothing:
template <typename T>
struct scientific_type {
T value;
explicit scientific_type(T val) : value{val} {}
};
- We can define completely custom formatting settings for any type that was wrapped into scientific_type before because if we overload the stream operator>> for it, the stream library executes completely different code when printing such types. This way, we can print scientific values in scientific floating-point notation, with uppercase formatting and explicit + prefix if they have positive values. We do also use our format_guard class in order to tidy up all our settings when leaving this function again:
template <typename T>
ostream& operator<<(ostream &os, const scientific_type<T> &w) {
format_guard _;
os << scientific << uppercase << showpos;
return os << w.value;
}
- In the main function, we will first play around with the format_guard class. We open a new scope, first get an instance of the class, and then we apply some wild formatting flags to std::cout:
int main()
{
{
format_guard _;
cout << hex << scientific << showbase << uppercase;
cout << "Numbers with special formatting:n";
cout << 0x123abc << 'n';
cout << 0.123456789 << 'n';
}
- After we printed some numbers with many formatting flags enabled, we left the scope again. While this happened, the destructor of format_guard tidied the formatting up. In order to test this, we are printing exactly the same numbers again. They should appear different:
cout << "Same numbers, but normal formatting again:n";
cout << 0x123abc << 'n';
cout << 0.123456789 << 'n';
- Now we put scientific_type to use. Let's print three floating-point numbers in a row. We wrap the second number in scientific_type. This way, it is printed in our special scientific style, but the numbers before and after it get default formatting. At the same time, we avoid ugly formatting line noise:
cout << "Mixed formatting: "
<< 123.0 << " "
<< scientific_type{123.0} << " "
<< 123.456 << 'n';
}
- Compiling and running the program yields us the following result. The first two numbers are printed with specific formatting. The next two numbers appear with default formatting, which shows us that our format_guard works just nicely. The three numbers in the last lines also look just as expected. Only the one in the middle has the formatting of scientific_type, the rest has default formatting:
$ ./pretty_print_on_the_fly
Numbers with special formatting:
0X123ABC
1.234568E-01
Same numbers, but normal formatting again:
1194684
0.123457
Mixed formatting: 123 +1.230000E+02 123.456