Restore the state of std::cout after manipulating it
Asked Answered
Y

9

147

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 'restore' the state of cout to its original one after returning from the function? (Somewhat like std::boolalpha and std::noboolalpha..) ?

Thanks.

Youngs answered 16/2, 2010 at 13:53 Comment(6)
I believe hex only lasts for the next shift out operation. The change is only persistent if you change the format flags manually instead of using manipulators.Unwearied
@BillyONeal: No, using manipulators has the same effect as changing the format flags manually. :-PDysphagia
If you are here due to a Covertiy finding Not restoring ostream format (STREAM_FORMAT_STATE), then see Coverity finding: Not restoring ostream format (STREAM_FORMAT_STATE).Citystate
I did something similar - see my question on Code Review: Use a standard stream, and restore its settings afterwards.Spinach
This question is a perfect example of why iostream is not better than stdio. Just found two nasty bugs because of not-/semi-/fully-/what-not persistent iomanip.Jocularity
For what it worth. Just found that std::setw affects only the next shift operation when compiled using g++ 4.9..0 with -std=c++11Portiere
L
133

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.

Longwinded answered 16/2, 2010 at 13:56 Comment(9)
Thanks! This is probably what I was looking for!Youngs
This didn't work for me. I needed const std::ostringstream default_format; ...; std::cout.copyfmt(default_format);.Dewie
@ChrisJester-Young, actually good C++ is RAII, especially in a case like this one!Paltry
@Alexis I 100% agree. See my answer (Boost IO Stream State Saver). :-)Dysphagia
THis is not exception-safe.Washtub
There's more to the stream state besides the flags.Citystate
You can avoid the problem by not pushing formats onto streams. Push the format and data into a temporary stringstream variable, then printAllogamy
Great that you linked the RAII answer[+] which answered the question I had after facing your helpful answer[+].Oof
DO NOT RESTORE std::setfillDownright
W
89

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
Wachter answered 21/6, 2015 at 19:18 Comment(4)
Although my original question has been answered a few years back, this answer is a great addition. :-)Youngs
@Youngs It appears to be a better solution, in which case, you can and probably should make it the accepted answer instead.Spirogyra
This for some reasons throws exception if exceptions are enabled for the stream. coliru.stacked-crooked.com/a/2a4ce6f5d3d8925bLoad
It seems that std::ios is always in bad state because it has NULL rdbuf. So setting a state with exceptions enabled causes exception throwing because of bad state. Solutions: 1) Use some class (for example std::stringstream) with rdbuf set instead of std::ios. 2) Save exceptions state separately to local variable and disable them before state.copyfmt, then restore exception from the variable (and do this again after restoring state from oldState which has exceptions disabled). 3) Set rdbuf to std::ios like this: struct : std::streambuf {} sbuf; std::ios oldState(&sbuf);Load
D
63

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;
}
Dysphagia answered 16/2, 2010 at 13:55 Comment(7)
Note that there's no magic here, that ios_flags_saver basically just saves and sets the flags like in @StefanKendall's answer.Washtub
@Washtub But it is exception-safe, unlike the other answer. ;-)Dysphagia
There's more to the stream state besides the flags.Citystate
@Citystate The IO Stream State Saver library has multiple classes, for saving different parts of the stream state, of which ios_flags_saver is just one.Dysphagia
Why for every small problem one should link against boost library...give me a break :DSporting
If you think it's worth reimplementing and maintaining every little thing by yourself, instead of using a reviewed, well tested library ...War
The link is now broken, here's the new address: boost.org/doc/libs/release/libs/io/doc/html/io.htmlEthelda
W
24

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;
}
Ward answered 16/9, 2013 at 7:51 Comment(4)
Excellent, if someone throws, you still got the correct flags in your stream.Paltry
There's more to the stream state besides the flags.Citystate
I really wish C++ allowed try/finally. This is an excellent example where RAII works, but finally would have been simpler.Iglesias
If your project is at least a bit sane, you have Boost and that comes with state savers for this purpose.Burmeister
M
14

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&.

Manhandle answered 3/5, 2017 at 22:59 Comment(7)
Good point[+], but it of course remembers to using std::stringstream for the formatting part as Mark Sherred pointed out.Oof
@Oof I'm not sure I get your point. An std::stringstream is an std:ostream, except using one introduces an extra intermediate buffer.Manhandle
Of course both are valid approaches to formatting output, both introduce a stream object, the one you describe is new to me. I've to think about pros and cons now. However, an inspiring question with enlightening answers ... (I mean the stream copy variant)Oof
You can't copy a stream, because copying buffers often doesn't make sense (e.g. stdout). However, you can have several stream objects for the same buffer, which is what this answer proposes to do. Whereas an std:stringstream will create its own independent std:stringbuf (an std::streambuf derivate), which then needs to be poured into std::cout.rdbuf()Manhandle
@Manhandle "The" std::ostream instance is typically provided by the client (or is global, e.g. std::cout), and the client/global ostream state is what needs to be maintained/restored when returning. The local/auto std::stringstream lets you isolate the state tweaks to a local std::ostream instead of manipulating the state of the client's std::ostream (or cout).Friedcake
@Friedcake again, you can do that without creating a stringbuf. you have not understood the answer; streams and buffers are separate thingsManhandle
Still a fantastic answer to learn from (and I've learned the lesson about “copying” streams). Those who are interested in the meaning of rd may read What does STL “rdbuf” method name stand for?Oof
E
10

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 to fmt::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

Elbertine answered 24/9, 2020 at 14:7 Comment(3)
Good to know, but as of almost April 2021, compilers don't support it, even though the standard is out there, maybe it's worth mentioning in this answer.Tabathatabb
I am very much waiting for this library to become standard shipping with C++ compilers.Costrel
Meanwhile, the standard is out and also implementations that support it. The most impressing thing I experienced was that a compile-time error indicates a mismatch between format string and arguments.Oof
S
9

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..
}
Secure answered 16/2, 2010 at 14:6 Comment(0)
B
1

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/

Branching answered 12/6, 2021 at 6:50 Comment(0)
L
0

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...

Lienlienhard answered 9/8, 2020 at 8:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.