【问题标题】:Implementing a no-op std::ostream实现无操作 std::ostream
【发布时间】:2010-10-20 02:32:23
【问题描述】:

我正在考虑创建一个日志记录类,其中包含 Info、Error 等可以配置输出到控制台、文件或无处的成员。

为了提高效率,我想避免格式化将被丢弃的消息的开销(即不以详细模式运行时的信息消息)。如果我实现一个自定义的 std::streambuf 输出到任何地方,我想 std::ostream 层仍将执行所有格式化。任何人都可以提出一种方法来获得真正的“空”std::ostream,从而避免对使用<< 传递给它的参数做任何工作吗?

【问题讨论】:

  • 我不会担心的。只需使用 neil 所示的空流。该类不需要任何更好的性能,因为显然如果您没有空目标,则必须进行格式化 ,因此这显然不是关键。只是我的 2 美分
  • 嗯,但它看起来像是一个“调试输出”的东西?我见过的一种方式是这样的:out() f const& operator

标签: c++ logging debugging


【解决方案1】:

一个 swift google 想出了这个可能有用的例子。我不提供任何保证,除了它编译和运行:-)

#include <streambuf>
#include <ostream>

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)
        {
            init(&m_sbuf);
        }

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

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

int main() {
    onullstream os;
    os << 666;
}

【讨论】:

  • +1。是的,我想如果你想将一个无操作的流传递给一个需要 ostream& 或 ostream* 参数的函数,那么从 std::basic_ostream 派生是必要的——Iraimbilanja 的技巧在那里不起作用。
  • 只能接受ostream *,不能接受ostream &amp;,对吗?
  • @athos std::ostream 是std::basic_ostream&lt;char, std::char_traits&lt;char&gt;&gt; 的别名。该类公开继承自该类
【解决方案2】:

谢谢分享代码,我只是做个测试,然后Neil的方法还是会做字符串格式化,例如:

#include <streambuf>
#include <ostream>
#include <iostream>
using namespace std;


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)
        {
            init(&m_sbuf);
        }

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

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

class MyClass
{
    int a;
    friend ostream& operator<< (ostream&, MyClass const&);
};

ostream& operator<<(ostream& out,MyClass const& b)
{
    std::cout<<"call format function!!";
    out << b.a;
    return out;
}

int main() {
    onullstream os;
    MyClass obj;
    os<<obj;
}

运行这个程序,你会发现“ostream& operator

【讨论】:

  • AFAIK 优化器可以删除认为没有副作用的代码。由于您对std::cout &lt;&lt; "call format function!!" 的调用确实有副作用,因此优化器不会删除它。但是,如果没有调用,它可能会被删除
【解决方案3】:

为了防止operator&lt;&lt;() 调用进行格式化,您应该在编译时知道流类型。这可以通过宏或模板来完成。

我的模板解决方案如下。

class NullStream {
public:
    void setFile() { /* no-op */ }
    template<typename TPrintable>
    NullStream& operator<<(TPrintable const&)
    { return *this; } /* no-op */
}

template<class TErrorStream> // add TInfoStream etc
class Logger {
public:
    TErrorStream& errorStream() {
        return m_errorStream;
    }

private:
    TErrorStream m_errorStream;
};

//usage
int main() {
    Logger<std::ofstream> normal_logger; // does real output
    normal_logger.errorStream().open("out.txt");
    normal_logger.errorStream() << "My age is " << 19;

    Logger<NullStream> null_logger; // does zero output with zero overhead
    null_logger.errorStream().open("out.txt"); // no-op
    null_logger.errorStream() << "My age is " << 19; // no-op
}

既然你必须在编译时这样做,那当然是相当不灵活的。

例如,您无法在运行时从配置文件中决定日志记录级别。

【讨论】:

  • +1:简单,干净,做得很好。请注意,例如“NullStream s; s
  • 我还要提一下,与 Neil 的 onullstream 不同,您的 NullStream 不能传递给需要 ostream& 或 ostream* 参数的函数。
  • expensive_function() 将肯定被评估,无论它位于何处。没有办法阻止宏和条件编译:) ...至于 Neil 的 onnullstream,我不相信它满足“零格式化开销”的要求:)
  • 是的,Neil 有不同的权衡。我想你的班级可以为 ostream& 提供一个转换函数,它返回 Neil 班级的一个实例,以获得两全其美;)
【解决方案4】:

您可能需要的不仅仅是文本格式和消息过滤。多线程呢?

我将实现过滤和多线程同步作为单独类的职责。

但是,日志记录不是那么简单的问题,我会尝试使用现有的日志记录解决方案,而不是开发新的解决方案。

【讨论】:

    【解决方案5】:

    为什么不使用数百万用户使用的现有日志记录解决方案? log4j、log4net、log4cxx..,仅举几例..

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-04
      • 1970-01-01
      • 2019-07-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多