实现no-op std :: ostream

时间:2009-04-17 12:55:46

标签: c++ logging debugging

我正在寻找一个日志类,其中包含Info,Error等成员,可以配置输出到控制台,文件或无处。

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

感谢。

5 个答案:

答案 0 :(得分:16)

一个迅速的谷歌提出了这个可能有用的例子。除了编译和运行之外,我不提供任何保证: - )

#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 :(得分:13)

所有,感谢分享代码,我只是做一个测试,然后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&amp; operator&lt;&lt;(ostream&amp; out,MyClass const&amp; b)”。因此,仍然会调用obj上的格式。因此,我们仍然无法避免格式化消息的开销。

答案 2 :(得分:4)

为防止operator<<()调用进行格式化,您应该在编译时知道streamtype。这可以通过宏或模板完成。

我的模板解决方案如下。

class NullStream {
public:
    void setFile() { /* no-op */ }
    template<typename TPrintable>
    NullStream& operator<<(TPrintable const&)
    { /* 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
}

由于您必须在编译时执行此操作,因此它当然非常不灵活。

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

答案 3 :(得分:0)

可能您需要的不仅仅是文本格式和邮件过滤。多线程怎么样?

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

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

答案 4 :(得分:0)

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