如何自动将流模式设置回默认值

How to automatically set stream mode back to default

C++ Steam 对象有状态。如果写一段代码,比如

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

忘记设置回流状态。这会导致其他一些不相关的代码出现问题。做 "set" 和 "set back" 对匹配很乏味。除此之外,在我看来,它也违反了 RAII 背后的约定。

我的问题是:是否有可能仅通过一层薄薄的包装,使这些状态操作类似于 RAII。也就是说,在表达式以分号结束后,流状态会自动设置回默认值。

更新:按照@0x499602D2 提供的link,一种解决方法可能类似于

#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)

然后就可以像

那样使用宏了
AUTO_COUT(cout << hex << setw(8) << setfill('0') << x << endl);

顺便说一句,为 boost::io::ios_state 的保护程序 class 添加一个锁定字段可能是个好主意,以防在多线程程序中发生有趣的事情。或者他们已经这样做了?

我曾经写过一个实用程序 class 供个人使用。 (我不知道它是否像提升代码一样完美,但它对我有用——所以,我敢于分享。)

#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;
}

ideone (life demo) 上编译和测试。

输出:

dec: 123
hex: deadbeef
123 in current: 7b
123 after recovered: 123

我将建议另一种方法。操纵器适用于 std::[i|o]stream 实例,但它们对 std::[i|o]stream 管理的 std::[i|o]streambuf 没有任何作用。

因此,您可以创建自己的 std::[i|o]stream,它将有自己的格式化状态,但写入同一个缓冲区 std::cout 使用:

#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";
}

输出:

20
32
20
32

Live on Coliru

这仅使用标准库中的功能,并且由于未触及原始流,因此应用操纵器是线程安全的(因为每个线程都在不同的流上运行)。现在,实际写入和读取的线程安全取决于托管流缓冲区的线程安全。