I created a Vector
class in C++ and it works great for my problems. I am now cleaning it up, and I ran into the following piece of code:
std::ostream& operator<<(std::ostream &output, const Vector &v){
output<<"["
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._x<<", "
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._y<<", "
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._z<<"]";
return output;
}
The code allows to print a vector as std::cout<<v<<std::endl;
. Each number has 23 spaces, of which 16 are the decimals. The text is right-aligned so that it will print:
1.123456123456e+01
-1.123456123456e+01
Instead of
1.123456123456e+01
-1.123456123456e+01
The code seems awfully repetitive. How can you "store" the format (all the setiosflags
, setw
and setprecision
statements) such that you can say something like "print the characters in a standard way, but the numbers with this given format".
Thank you!
Edit
As per Rob Adams' comment, I changed my ugly code (which, as pointed out by others, would mess up the precision for the "next guy") to a more succinct (and correct):
std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
output<<"["
<<std::setw(23)<<v._x<<", "
<<std::setw(23)<<v._y<<", "
<<std::setw(23)<<v._z
<<"]";
output.flags(f);
output.precision(p);
return output;
}
The iomanip is a library in C++ which helps us in manipulating the output of any C++ program. There are many functions in this library that help in manipulating the output. To name a few we have functions to reset flags, set fill characters, set precision, get date and time, etc.
The header <iomanip> contains the functions that we can use to format the output of the C++ program. These functions can be used one at a time or together to make the output of our program more presentable.
The header <iomanip> is part of the Input/output library of the C++ Standard Library. It defines the manipulator functions resetiosflags() , setiosflags() , setbase() , setfill() , setprecision() , and setw() . These functions may be conveniently used by C++ programs to affect the state of iostream objects.
iomanip: iomanip stands for input-output manipulators. The methods declared in these files are used for manipulating streams.
Only std::setw()
is temporary. The other two calls, setiosflags
, and setprecision
have a permanent effect.
So, you could change your code to :
std::ostream& operator<<(std::ostream &output, const Vector &v){
output<<"["
<<std::setiosflags(std::ios::right | std::ios::scientific)
<<std::setw(23)
<<std::setprecision(16)
<<v._x<<", "
<<std::setw(23)
<<v._y<<", "
<<std::setw(23)
<<v._z<<"]";
return output;
}
But now you've borked the flags and precision for the next guy. Try this instead:
std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
output<<"["
<<std::setw(23)
<<v._x<<", "
<<std::setw(23)
<<v._y<<", "
<<std::setw(23)
<<v._z<<"]";
output.flags(f);
output.precision(p);
return output;
}
Finally, if you absolutely have to get rid of the duplication of the constant 23
, you could do something like this (but I wouldn't recommend it):
struct width {
int w;
width(int w) : w(w) {}
friend std::ostream& operator<<(std::ostream&os, const width& w) {
return os << std::setw(width.w);
}
};
std::ostream& operator<<(std::ostream &output, const Vector &v){
std::ios_base::fmtflags f = output.flags(std::ios::right | std::ios::scientific);
std::streamsize p = output.precision(16);
width w(23);
output<<"["
<<w
<<v._x<<", "
<<w
<<v._y<<", "
<<w
<<v._z<<"]";
output.flags(f);
output.precision(p);
return output;
}
See also this other question, where they decided that you can't make width permanent.
In C++20 you'll be able to do:
std::ostream& operator<<(std::ostream& output, const Vector& v){
const int width = 23, precision = 16;
return output << std::format(
"[{0:{3}.{4}e}, {1:{3}.{4}e}, {2:{3}.{4}e}]",
v._x, v._y, v._z, width, precision);
}
Unlike I/O manipulators, std::format
will not change ostream
's formatting state saving your from the problem mentioned by Bo Persson:
Your real problem is what happens to the next output after this one...
It will also work I/O manipulators work correctly with Vector
.
Until std::format
is available you can use the {fmt} library it is based on.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With