如何获取被std::osyncstream包裹的std::ostream的流状态
问题背景
需要提取被std::osyncstream包裹的std::ostream的iostate(goodbit/badbit/failbit),以判断输出操作是否成功或流是否处于错误状态。直接操作ostream时,可通过fail()/good()/bad()直接获取状态,但用osyncstream包裹后,即使流状态理应变化,这些状态位始终保持初始值。
直接操作ostream的示例
std::ofstream logFile("file1.txt"); std::ostream oStream(logFile.rdbuf()); { std::ofstream logFile2("file2.txt"); oStream.rdbuf(logFile2.rdbuf()); } std::cout << "oStream.fail(): " << oStream.fail() << "\n"; std::cout << "oStream.good(): " << oStream.good() << "\n"; std::cout << "oStream.bad(): " << oStream.bad() << "\n"; oStream << "This is probably UB because used streambuf is gone" << std::endl; std::cout << "oStream.fail(): " << oStream.fail() << "\n"; std::cout << "oStream.good(): " << oStream.good() << "\n"; std::cout << "oStream.bad(): " << oStream.bad() << "\n";
使用osyncstream后的异常情况
std::ofstream logFile("file1.txt"); std::ostream oStream(logFile.rdbuf()); { std::ofstream logFile2("file2.txt"); oStream.rdbuf(logFile2.rdbuf()); } std::osyncstream osyncStream(oStream); std::cout << "oStream.fail(): " << oStream.fail() << "\n"; std::cout << "oStream.good(): " << oStream.good() << "\n"; std::cout << "oStream.bad(): " << oStream.bad() << "\n"; std::cout << "osyncStream.fail(): " << osyncStream.fail() << "\n"; std::cout << "osyncStream.good(): " << osyncStream.good() << "\n"; std::cout << "osyncStream.bad(): " << osyncStream.bad() << "\n"; osyncStream << "This is probably UB because used streambuf is gone" << std::endl; std::cout << "oStream.fail(): " << oStream.fail() << "\n"; std::cout << "oStream.good(): " << oStream.good() << "\n"; std::cout << "oStream.bad(): " << oStream.bad() << "\n"; std::cout << "osyncStream.fail(): " << osyncStream.fail() << "\n"; std::cout << "osyncStream.good(): " << osyncStream.good() << "\n"; std::cout << "osyncStream.bad(): " << osyncStream.bad() << "\n";
原因分析
std::osyncstream的核心特性是异步缓冲:它会将输出内容暂存在内部缓冲区中,直到调用emit()/flush()或对象被销毁时,才会将内容同步到底层ostream。在此之前,底层流的状态不会被更新,osyncstream自身也不会暴露底层流的错误状态。
解决方案
1. 显式同步并检测状态
通过osyncstream::emit()强制将缓冲区内容同步到底层流,之后直接检查底层ostream的状态,或者使用osyncstream::get_wrapped()获取底层流对象进行检测:
std::osyncstream osyncStream(oStream); osyncStream << "Test output" << std::endl; osyncStream.emit(); // 强制同步缓冲区到ostream // 检测底层流状态 std::cout << "oStream.fail(): " << oStream.fail() << "\n"; std::cout << "oStream.bad(): " << oStream.bad() << "\n"; // 或通过get_wrapped()获取底层流 auto& wrappedStream = osyncStream.get_wrapped(); std::cout << "wrappedStream.good(): " << wrappedStream.good() << "\n";
2. 利用osyncstream生命周期自动同步
当osyncstream对象被销毁时,会自动调用emit()完成同步,此时底层流的状态会被更新。因此可以在osyncstream生命周期结束后检测底层流状态:
{ std::osyncstream osyncStream(oStream); osyncStream << "Test output" << std::endl; } // osyncStream销毁,自动同步 // 此时检测底层流状态 std::cout << "oStream.fail(): " << oStream.fail() << "\n";
针对最终目标:检测ostream是否提前超出作用域
如果核心需求是监控调用者提供的ostream(或其底层streambuf)是否在类使用期间被销毁,可以采用以下方案:
方案1:自定义监控streambuf
实现一个包装类继承自std::streambuf,持有原streambuf的指针,并在原streambuf销毁时标记错误状态。类中使用这个自定义streambuf来检测原流的生命周期:
class MonitoredStreambuf : public std::streambuf { public: explicit MonitoredStreambuf(std::streambuf* sb) : m_sb(sb), m_alive(true) {} ~MonitoredStreambuf() { m_alive = false; } bool isAlive() const { return m_alive; } // 重写必要的streambuf方法,转发到原sb int overflow(int c) override { return m_sb ? m_sb->overflow(c) : EOF; } int sync() override { return m_sb ? m_sb->sync() : -1; } private: std::streambuf* m_sb; bool m_alive; }; // 使用时 class MyClass { public: explicit MyClass(std::ostream& os) : m_monitor(os.rdbuf()), m_os(os) { m_os.rdbuf(&m_monitor); } bool isStreamValid() const { return m_monitor.isAlive() && !m_os.fail(); } private: MonitoredStreambuf m_monitor; std::ostream& m_os; };
方案2:持有ostream的智能指针
要求调用者提供std::shared_ptr<std::ostream>而非原始引用,确保流的生命周期至少与你的类一致,避免悬垂引用问题。
内容的提问来源于stack exchange,提问作者kfpgk

