38

If I apply an arbitrary number of manipulators to a stream, is there a way to undo the application of those manipulators in a generic way?

For example, consider the following:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    cout << "Hello" << hex << 42 << "\n";
    // now i want to "roll-back" cout to whatever state it was in
    // before the code above, *without* having to know 
    // what modifiers I added to it

    // ... MAGIC HAPPENS! ...

    cout << "This should not be in hex: " << 42 << "\n";
}

Suppose I want to add code at MAGIC HAPPENS that will revert the state of the stream manipulators to whatever it was before I did cout << hex. But I don't know what manipulators I added. How can I accomplish this?

In other words, I'd like to be able to write something like this (psudocode/fantasy code):

std::something old_state = cout.current_manip_state();
cout << hex;
cout.restore_manip_state(old_state);

Is this possible?

EDIT:

In case you're curious, I'm interested in doing this in a custom operator<<() I'm writing for a complex type. The type is a kind of discriminated union, and different value types will have different manips applied to the stream.

EDIT2:

Restriction: I cannot use Boost or any other 3rd party libraries. Solution must be in standard C++.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
John Dibling
  • 99,718
  • 31
  • 186
  • 324
  • Saving and restoring __all__ of a stream's state is quite elaborate. There's a state saver for streams by James Kanze out there somewhere on the web. (In general, when you need to know something about streams that isn't answered in [Langer & Kreft](http://www.angelikalanger.com/IOStreams/Reviews/devx.htm), try to find a statement by James Kanze or Dietmar Kühl. They're definitive.) – sbi Nov 18 '10 at 21:53
  • "_I cannot use Boost or any other 3rd party libraries._" Why not? – curiousguy Nov 19 '11 at 18:56
  • @curiousguy: It's too much to go in to in any depth in a comment, but long story short, we simply don't use Boost in our production environment. Basically, it's more than we want to maintain. – John Dibling Nov 21 '11 at 17:00
  • Boost is (a) huge, (b) monolithic, (c) distributed as a tarball instead of in one of the many sensible ways that have been developed since Boost's inception. – Ian Ni-Lewis Nov 12 '15 at 14:49
  • 1
    @IanNi-Lewis: To be fair, on many Linux distros (RH, Ubuntu are 2 examples), there are packages you can install fairly simply from the package manager. – John Dibling Nov 16 '15 at 18:48

5 Answers5

59

Yes.

You can save the state and restore it:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{

    std::ios  state(NULL);
    state.copyfmt(std::cout);

    cout << "Hello" << hex << 42 << "\n";
    // now i want to "roll-back" cout to whatever state it was in
    // before the code above, *without* having to know what modifiers I added to it

  // ... MAGIC HAPPENS! ...

    std::cout.copyfmt(state);
    cout << "This should not be in hex: " << 42 << "\n";
}

If you want to get back to the default state you don't even need to save the state you can extract it from a temporary object.

std::cout.copyfmt(std::ios(NULL));
Martin York
  • 257,169
  • 86
  • 333
  • 562
  • ": Does `copyfmt` include all of the flags specified by @Charles' answer below? – John Dibling Feb 04 '11 at 23:39
  • @John Dibling: Don't have my standard here. But according to this [copyfmt](http://www.cplusplus.com/reference/iostream/ios/copyfmt/) it copies everything but the state flags. Which includes (format flags/prec/width and a few other things) – Martin York Feb 05 '11 at 00:56
  • Why `NULL` and not `nullptr`? – Zereges Sep 30 '17 at 11:56
  • 2
    @Zereges: Because this code was written in 2010 before `nulllptr` existed. – Martin York Feb 12 '18 at 18:34
  • This for some reasons throws exception if exceptions are enabled for the stream. http://coliru.stacked-crooked.com/a/2a4ce6f5d3d8925b – anton_rh Dec 29 '18 at 13:10
  • 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);` – anton_rh Jan 09 '19 at 10:31
  • For the 2: http://coliru.stacked-crooked.com/a/e52e90a13a75e95f – anton_rh Jan 09 '19 at 10:58
17

The standard manipulators all manipulate a stream's format flags, precision and width settings. The width setting is reset by most formatted output operations anyway. These can all be retrieved like this:

std::ios_base::fmtflags saveflags = std::cout.flags();
std::streamsize prec = std::cout.precision();
std::streamsize width = std::cout.width();

and restored:

std::cout.flags( saveflags );
std::cout.precision( prec );
std::cout.width( width );

Turning this into an RAII class is an exercise for the reader...

CB Bailey
  • 755,051
  • 104
  • 632
  • 656
15

Saving and restoring state is not exception-safe. I would propose to shuffle everything into a stringstream, and finally you put that on the real stream (which has never changed its flags at all).

#include <iostream>
#include <iomanip>
#include <sstream>

int main()
{
    std::ostringstream out;
    out << "Hello" << std::hex << 42 << "\n";
    std::cout << out.str();

    // no magic necessary!

    std::cout << "This should not be in hex: " << 42 << "\n";
}

Of course this is a little less performant. The perfect solutions depends on your specific needs.

fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • 2
    +1 good suggestion. alas this code will be called perhaps 5 million times per second, so i'd rather not construct a new stream :) – John Dibling Nov 18 '10 at 18:25
13

Boost IO State saver might be of help.

http://www.boost.org/doc/libs/1_40_0/libs/io/doc/ios_state.html

Naveen
  • 4,456
  • 2
  • 22
  • 16
  • +1 good suggestion, alas I can't use boost in my particular environment – John Dibling Nov 18 '10 at 17:38
  • 1
    Ok. I think this is a pure header only file, you will need to track the linked headers, but I think it is about 3-4 headers. So worth a try. Otherwise, I suspect you will have to keep track of the individual modifiers and then restore them. – Naveen Nov 18 '10 at 17:45
5

I know that is an old question, but for future generations:

You can also write a simple state saver yourself (it will certainly help you avoid leaving the state changed). Just use the solution suggested by @loki and run it from the constructor/destructor of an object (in short: RAII) along these lines:

class stateSaver
{
  public:
    stateSaver(ostream& os): stream_(os), state_(nullptr) { state_.copyfmt(os); }
    ~stateSaver() { stream_.copyfmt(state_); }
  private:
    std::ios state_;
    ostream& stream_;
};

Then, you will use it like this:

void myFunc() {
    stateSaver state(cout);
    cout << hex << 42 << endl; // will be in hex
}

int main() {
    cout << 42 << endl; // will be in dec
    myFunc();
    cout << 42 << endl; // will also be in dec
}
Oren S
  • 1,820
  • 1
  • 19
  • 33