将调试输出重定向到空流而不是 std::cerr

问题描述 投票:0回答:2

我正在使用的软件库将大量调试输出写入

std::cerr
,但如果我告诉它安静,则将该输出重定向到空流。这是一个简化的
main.cpp
,显示了代码如何尝试实现这一点:

#include <iostream>
#include <fstream>
#include <cassert>

// The stream that debug output is sent to. By default
// this points to std::cerr.
std::ostream* debugStream(&std::cerr);

// Throughout the library's codebase this function is called
// to get the stream that debug output should be sent to.
std::ostream& DebugStream()
{
    return *debugStream;
}

// Null stream. This file stream will never be opened and acts
// as a null stream for DebugStream().
std::ofstream nullStream;

// Redirects debug output to the null stream
void BeQuiet()
{
    debugStream = &nullStream;
}

int main(int argc, char** argv)
{
  DebugStream() << "foo" << std::endl;
  BeQuiet();
  DebugStream() << "bar" << std::endl;
  assert(debugStream->good());

  return 0;
}

当你运行这个程序时,你会注意到字符串“bar”被正确地发送到空流。但是,我注意到断言失败了。这是我应该关心的事情吗?或者这只是库开发人员选择的方法的一个稍微丑陋的细节?

如果您愿意,欢迎提出更好的替代方案建议。一些限制:

  • 库是跨平台的,所以我认为使用 opening
    /dev/null
    不是一个有效的解决方案,因为它在 Windows 上不起作用
  • 库使用标准 C++,因此任何替代解决方案都不应使用特定于编译器的内容
c++ cross-platform iostream
2个回答
4
投票

没有真正需要担心流不是

good()
!由于输出操作符在故障模式下实际上并没有对流做任何事情,因此记录的不同实体没有格式化,即,与其他方法相比,代码确实运行得更快。

请注意,您实际上不需要第二个流来禁用输出:

  1. 假设所有输出操作符都表现良好,您可以设置

    std::ios_base::failbit

    debugStream().setstate(std::ios_base::failbit);
    
  2. 如果有错误的输出写入流,即使它不是

    good()
    ,您可以将其流缓冲区设置为空:

    debugStream().rdbuf(nullptr);

如果你真的想让你的流保持在

good()
状态,你会安装一个只消耗字符的流缓冲区。但是请注意,您希望为该流缓冲区提供一个缓冲区,因为为每个
overflow()
调用
char
是相当昂贵的。此外,覆盖
xsputn()
是一个好处:

struct nullbuf
    : std::streambuf {
    char buf[256];
    int overflow(int c) {
        this->setp(this->buf, this->buf + 256);
        return std::char_traits<char>::not_eof(c);
    }
    std::streamsize xsputn(char const* , std::streamsize n) override {
        return n;
     }
};
...
nullbuf sbuf;
debugStream().rdbuf(&sbuf);
...
debugStream().rdbuf(0);

有必要重置流的流缓冲区,因为

std::ostream
的析构函数将刷新 stresm 缓冲区(即,它调用
pubsync()
)。在被破坏的流缓冲区上这样做是行不通的。

就个人而言,我会选择设置

std::ios_base::failbit
.


3
投票

this answer你找到一个通用的助手来将任何流重定向到任何其他流:

class stream_redirector {
public:
    stream_redirector(std::ios& stream, std::streambuf* newBuf) :
        savedBuf_(stream.rdbuf()), stream_(stream)
    {
        stream_.rdbuf(newBuf);
    }

    ~stream_redirector() {
        stream_.rdbuf(savedBuf_);
    }

private:
    std::streambuf* savedBuf_;
    std::ios& stream_;
};

现在您所需要的只是来自this answer的无操作流,它会丢弃任何东西:

template <class cT, class traits = std::char_traits<cT> >
class basic_nullbuf: public std::basic_streambuf<cT, traits> {
    typename traits::int_type overflow(typename traits::int_type c)
    {
        return traits::not_eof(c); // indicate success
    }
};

template <class cT, class traits = std::char_traits<cT> >
class basic_onullstream: public std::basic_ostream<cT, traits> {
public:
    basic_onullstream():
    std::basic_ios<cT, traits>(&m_sbuf),
    std::basic_ostream<cT, traits>(&m_sbuf)
    {
        // note: the original code is missing the required this->
        this->init(&m_sbuf);
    }

private:
    basic_nullbuf<cT, traits> m_sbuf;
};

typedef basic_onullstream<char> onullstream;
typedef basic_onullstream<wchar_t> wonullstream;

你很高兴:

int main(int argc, char** argv)
{
    std::cerr << "foo" << std::endl;
    {
        onullstream nos;
        stream_redirector sr( std::cerr, nos.rdbuf() );
        std::cerr << "bar" << std::endl;
    }
    std::cerr << "baz" << std::endl;
}

最后这里有一个活生生的例子,以防你想玩它。这种方法还有一个额外的好处,就是您(和您的同事)仍然可以在您的代码中使用

std::cerr
,并且您可以随意将其关闭和打开:)

© www.soinside.com 2019 - 2024. All rights reserved.