Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"Roll-Back" or Undo Any Manipulators Applied To A Stream Without Knowing What The Manipulators Were [duplicate]

Tags:

c++

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

like image 431
John Dibling Avatar asked Nov 18 '10 17:11

John Dibling


5 Answers

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));
like image 162
Martin York Avatar answered Nov 11 '22 12:11

Martin York


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

like image 43
CB Bailey Avatar answered Nov 11 '22 14:11

CB Bailey


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.

like image 44
fredoverflow Avatar answered Nov 11 '22 14:11

fredoverflow


Boost IO State saver might be of help.

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

like image 40
Naveen Avatar answered Nov 11 '22 14:11

Naveen


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
}
like image 23
Oren S Avatar answered Nov 11 '22 13:11

Oren S