如何自动将流模式设置回默认值
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
这仅使用标准库中的功能,并且由于未触及原始流,因此应用操纵器是线程安全的(因为每个线程都在不同的流上运行)。现在,实际写入和读取的线程安全取决于托管流缓冲区的线程安全。
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
这仅使用标准库中的功能,并且由于未触及原始流,因此应用操纵器是线程安全的(因为每个线程都在不同的流上运行)。现在,实际写入和读取的线程安全取决于托管流缓冲区的线程安全。