5

C++ steam objects have state. If one write a piece of code like

using namespace std; 
cout << hex << setw(8) << setfill('0') << x << endl;

forgetting setting the stream state back. This will cause problems in some other unrelated codes. It's tedious to do "set" and "set back" pair matching. Besides from that, it seems to me it's also against convention behind RAII.

My question is: is it possible, with only a thin layer of wrapping, to make those state manipulations RAII-like. That is, right after the end of an expression by semicolon, stream state is automatically set back to default.

Update: Following the link provided by @0x499602D2, one workaround might be something like

#include <boost/io/ios_state.hpp>
#include <ios>
#include <iostream>
#include <ostream>
#define AUTO_COUT(x) {\
    boost::io::ios_all_saver  ias( cout );\
    x;\
    }while(0)

Then one can use the macro like

AUTO_COUT(cout << hex << setw(8) << setfill('0') << x << endl);

BTW, it might be a good idea to add a lock field to those saver class of boost::io::ios_state, in case funny things occur in a multi-threading program. Or they have already done so?

John Z. Li
  • 1,893
  • 2
  • 12
  • 19

2 Answers2

5

I'm going to suggest an alternative approach. The manipulators apply to the std::[i|o]stream instance, but they do nothing with regards to the std::[i|o]streambuf which is managed by that std::[i|o]stream.

Therefore, you can create your own std::[i|o]stream, which will have its own formatting state, but writing to the same buffer std::cout uses:

#include <iostream>
#include <iomanip>

int main()
{
    std::cout << std::hex << 32 << "\n";
    std::ostream os(std::cout.rdbuf());
    os << 32 << "\n" << std::hex;
    std::cout << std::dec;
    os << 32 << "\n";
    std::cout << 32 << "\n";
}

Output:

20
32
20
32

Live on Coliru

This uses only features from standard library, and since the original stream is not touched, applying manipulators is trivially thread safe (because each thread operates on a different stream). Now, the actual writes and reads' thread safety depends on the thread safety of the managed stream buffer.

milleniumbug
  • 15,379
  • 3
  • 47
  • 71
  • 1
    This is nice, especially, as it considers the (per comment added) requirement(?) for thread-safety. Though, _Now, the actual writes and reads' thread safety depends on the thread safety of the managed stream buffer._ which made me curios. I believe, to be sure, you had to provide an own, thread-safe streambuf. Then, something else came into my mind: A thread-safe streambuf may grant thread-safety (what's surely not bad for a robust appl.) but not that output of concurrent threads isn't mixed accidentally. For the latter, output operations (as whole) had to be guarded (e.g. by a mutex). – Scheff's Cat Feb 08 '18 at 08:47
  • 1
    @Scheff Wasn't sure about the guarantees of the streambuf classes, but if they're actually thread-safe, then, yes, this remark can be removed. Wrt I/O intermixing, `std::syncbuf` class proposed for C++20 may help with it http://en.cppreference.com/w/cpp/io/basic_syncbuf – milleniumbug Feb 08 '18 at 08:53
  • @milleniumbug The standard guarantees that the standard streams are thread-safe if `sync_with_stdio` is set (which is the default). However, it does not guarantee that their stream buffers are thread-safe when used from other streams. And, as Scheff said, preventing interleaving requires external synchronization anyway, so it just seems like a good idea to use it. – Arne Vogel Feb 08 '18 at 11:08
2

I once wrote a utility class for my personal use. (I don't know whether it is as perfect as the boost code probably is but it worked for me – so, I dare to share.)

#include <iostream>
#include <iomanip>

/** provides a helper class to work with streams.
 *
 * It stores current format states of a stream in constructor and
 * recovers these settings in destructor.
 *
 * Example:
 * <pre>
 * { // backup states of std::cout
 *   IOSFmtBackup stateCOut(std::cout);
 *   // do some formatted output
 *   std::cout
 *     << "dec: " << std::dec << 123 << std::endl
 *     << "hex: " << std::hex << std::setw(8) << std::setfill('0')
 *     << 0xdeadbeef << std::endl;
 * } // destruction of stateCOut recovers former states of std::cout
 * </pre>
 */
class IOSFmtBackup {

  // variables:
  private:
    /// the concerning stream
    std::ios &_stream;
    /// the backup of formatter states
    std::ios _fmt;

  // methods:
  public:
    /// @name Construction & Destruction
    //@{

    /** constructor.
     *
     * @param stream the stream for backup
     */
    explicit IOSFmtBackup(std::ios &stream):
      _stream(stream), _fmt(0)
    {
      _fmt.copyfmt(_stream);
    }

    /// destructor.
    ~IOSFmtBackup() { _stream.copyfmt(_fmt); }

    // disabled:
    IOSFmtBackup(const IOSFmtBackup&) = delete;
    IOSFmtBackup& operator=(const IOSFmtBackup&) = delete;
    //@}
};

int main()
{
  { // backup states of std::cout
    IOSFmtBackup stateCOut(std::cout);
    // do some formatted output
    std::cout
      << "dec: " << std::dec << 123 << std::endl
      << "hex: " << std::hex << std::setw(8) << std::setfill('0')
      << 0xdeadbeef << std::endl
      << "123 in current: " << 123 << std::endl;
  } // destruction of stateCOut recovers former states of std::cout
  // check whether formatting is recovered
  std::cout << "123 after recovered: " << 123 << std::endl;
  return 0;
}

Compiled and tested on ideone (life demo).

Output:

dec: 123
hex: deadbeef
123 in current: 7b
123 after recovered: 123
Scheff's Cat
  • 19,528
  • 6
  • 28
  • 56