Suppose I have a code like this:
void printHex(std::ostream& x){
x<<std::hex<<123;
}
..
int main(){
std::cout<<100; // prints 100 base 10
printHex(std::cout); //prints 123 in hex
std::cout<<73; //problem! prints 73 in hex..
}
My question is if there is any way to 'res开发者_运维知识库tore' the state of cout
to its original one after returning from the function? (Somewhat like std::boolalpha
and std::noboolalpha
..) ?
Thanks.
you need to #include <iostream>
or #include <ios>
then when required:
std::ios_base::fmtflags f( cout.flags() );
//Your code here...
cout.flags( f );
You can put these at the beginning and end of your function, or check out this answer on how to use this with RAII.
Note that the answers presented here won't restore the full state of std::cout
. For example, std::setfill
will "stick" even after calling .flags()
. A better solution is to use .copyfmt
:
std::ios oldState(nullptr);
oldState.copyfmt(std::cout);
std::cout
<< std::hex
<< std::setw(8)
<< std::setfill('0')
<< 0xDECEA5ED
<< std::endl;
std::cout.copyfmt(oldState);
std::cout
<< std::setw(15)
<< std::left
<< "case closed"
<< std::endl;
Will print:
case closed
rather than:
case closed0000
The Boost IO Stream State Saver seems exactly what you need. :-)
Example based on your code snippet:
void printHex(std::ostream& x) {
boost::io::ios_flags_saver ifs(x);
x << std::hex << 123;
}
I've created an RAII class using the example code from this answer. The big advantage to this technique comes if you have multiple return paths from a function that sets flags on an iostream. Whichever return path is used, the destructor will always be called and the flags will always get reset. There is no chance of forgetting to restore the flags when the function returns.
class IosFlagSaver {
public:
explicit IosFlagSaver(std::ostream& _ios):
ios(_ios),
f(_ios.flags()) {
}
~IosFlagSaver() {
ios.flags(f);
}
IosFlagSaver(const IosFlagSaver &rhs) = delete;
IosFlagSaver& operator= (const IosFlagSaver& rhs) = delete;
private:
std::ostream& ios;
std::ios::fmtflags f;
};
You would then use it by creating a local instance of IosFlagSaver whenever you wanted to save the current flag state. When this instance goes out of scope, the flag state will be restored.
void f(int i) {
IosFlagSaver iosfs(std::cout);
std::cout << i << " " << std::hex << i << " ";
if (i < 100) {
std::cout << std::endl;
return;
}
std::cout << std::oct << i << std::endl;
}
You can create another wrapper around the stdout buffer:
#include <iostream>
#include <iomanip>
int main() {
int x = 76;
std::ostream hexcout (std::cout.rdbuf());
hexcout << std::hex;
std::cout << x << "\n"; // still "76"
hexcout << x << "\n"; // "4c"
}
In a function:
void print(std::ostream& os) {
std::ostream copy (os.rdbuf());
copy << std::hex;
copy << 123;
}
Of course if performance is an issue this is a bit more expensive because it's copying the entire ios
object (but not the buffer) including some stuff that you're paying for but unlikely to use such as the locale.
Otherwise I feel like if you're going to use .flags()
it's better to be consistent and use .setf()
as well rather than the <<
syntax (pure question of style).
void print(std::ostream& os) {
std::ios::fmtflags os_flags (os.flags());
os.setf(std::ios::hex);
os << 123;
os.flags(os_flags);
}
As others have said you can put the above (and .precision()
and .fill()
, but typically not the locale and words-related stuff that is usually not going to be modified and is heavier) in a class for convenience and to make it exception-safe; the constructor should accept std::ios&
.
With a little bit of modification to make the output more readable :
void printHex(std::ostream& x) {
ios::fmtflags f(x.flags());
x << std::hex << 123 << "\n";
x.flags(f);
}
int main() {
std::cout << 100 << "\n"; // prints 100 base 10
printHex(std::cout); // prints 123 in hex
std::cout << 73 << "\n"; // problem! prints 73 in hex..
}
C++20 std::format
will be a superior alternative to save restore in most cases
Once you can use it, you will e.g. be able to write hexadecimals simply as:
#include <format>
#include <string>
int main() {
std::cout << std::format("{:x} {:#x} {}\n", 16, 17, 18);
}
Expected output:
10 0x11 18
This will therefore completely overcome the madness of modifying std::cout
state.
The existing fmt
library implements it for before it gets official support: https://github.com/fmtlib/fmt Install on Ubuntu 22.04:
sudo apt install libfmt-dev
Modify source to replace:
<format>
with<fmt/core.h>
std::format
tofmt::format
main.cpp
#include <iostream>
#include <fmt/core.h>
int main() {
std::cout << fmt::format("{:x} {:#x} {}\n", 16, 17, 18);
}
and compile and run with:
g++ -std=c++11 -o main.out main.cpp -lfmt
./main.out
Output:
10 0x11 18
Related: std::string formatting like sprintf
Instead of injecting format into cout, the <<
way, adopting setf
and unsetf
could be a cleaner solution.
void printHex(std::ostream& x){
x.setf(std::ios::hex, std::ios::basefield);
x << 123;
x.unsetf(std::ios::basefield);
}
the ios_base namespace works fine too
void printHex(std::ostream& x){
x.setf(std::ios_base::hex, std::ios_base::basefield);
x << 123;
x.unsetf(std::ios_base::basefield);
}
Reference: http://www.cplusplus.com/reference/ios/ios_base/setf/
I would like to generalize the answer from qbert220 somewhat:
#include <ios>
class IoStreamFlagsRestorer
{
public:
IoStreamFlagsRestorer(std::ios_base & ioStream)
: ioStream_(ioStream)
, flags_(ioStream_.flags())
{
}
~IoStreamFlagsRestorer()
{
ioStream_.flags(flags_);
}
private:
std::ios_base & ioStream_;
std::ios_base::fmtflags const flags_;
};
This should work for input streams and others as well.
PS: I would have liked to make this simply a comment to above answer, stackoverflow however does not allow me to do so because of missing reputation. Thus make me clutter the answers here instead of a simple comment...
精彩评论