Should I create a temporary ostream using another's streambuf?
Asked Answered
M

3

10

Suppose I have a function that takes an ostream & parameter o and writes to that ostream. An operator << implementation would be a good example.

ostream& operator << (ostream& o, const MyThing& t)
{
  // ... interesting code here ...
  return o;
}

Within the function, I might want to specify formatting options on the stream. For example, I might want a number to be printed as hex, no matter how the o is configured when it is passed in to the function.

Secondly, I might want to be able to make assumptions about the current formatting flags. For example, it would be nice to be able to assume that numbers were formatted as decimal unless I request otherwise.

Finally, by the time the function exits I want the formatting options on o to be the same as they were before the function was called, so as to appear unchanged to the caller. This is simply a matter of politeness to the caller.

Up until now I have achieved this by creating a local ostringstream within the function, doing all my work on that (including setting formatting options), and sending the .str() to o at the end of the function. The StackOverflow question here suggests that people cleverer than me take the same approach. However, it bothers me that I'm keeping so much data in ostringstreams that could perhaps be sent to the output earlier (the strings can get quite large).

I have two questions:

1) Is it legal, idiomatic, good form, etc. to create a temporary (stack based) ostream around o.rdbuf() and do my work on that ostream? My own tests and the page at cppreference.com appears to suggest that I can.

ostream& operator << (ostream& o_, const MyThing& t)
{
  ostream o (o_.rdbuf());
  // write stuff to "o",
  // setting formatting options as I go.
  return o_; // Formatting on the parameter ostream o_ unchanged.
}

2) Is there another, better way that I have not considered?

Meson answered 6/11, 2014 at 11:48 Comment(0)
C
2

Boost IO State Savers are built exactly for this purpose.

Communicable answered 6/11, 2014 at 12:7 Comment(0)
L
1

That's not a bad solution; it's certainly legal. I don't think it's too common, so it's probably a good idea to comment as to why you're doing it.

The most frequent solution I've seen here is to create a state saver class, which will save all of the state you need (typically, flags(), precision() and fill()) in the constructor, and restore it in the destructor, and then to forceably set all of the options you want. (It may be possible to use copyfmt for this, although this also copies things like the exception mask, which you probably don't want to play with.)

Leclerc answered 6/11, 2014 at 12:23 Comment(0)
G
0

The settings can be stored in a type of object called a fmtflags object, defined in a class called ios, which is included with iostream. You can declare one of these objects, but you have to declare it using the scope resolution operator.

The following statement will save certain aspects of the format state in the variable old_settings:

ios::fmtflags old_settings = cout.flags();

Then, after doing the output using the new setting, you can restore the old setting by calling the same function with the old settings as an argument:

cout.flags(old_settings);

Other settings can be obtained and restored with member functions. For example,

int old_precision = cout.precision();

will save the current precision specification. Then

cout.precision(old_precision);

will restore the precision to the original value

Grearson answered 6/11, 2014 at 12:7 Comment(1)
Those calls would best be placed in the constructor and destructor of some stack-based object. I assume that's what the Boost classes do.Meson

© 2022 - 2024 — McMap. All rights reserved.