停止评估运算符<<

时间:2011-12-22 10:28:25

标签: c++ logging

我正在尝试为我自己做一个简单的日志库。我知道有几次,但我没有找到任何只有标题,小和非常“c ++”的日志库,它适合我的应用程序。

目前我有以下语法:

logger << debug << "A debug message" << end; //debug and end is my custom manipulators

我已经实现了所有必要的运算符&lt;&lt;并且它工作得很好,特别是当它与std :: ostream具有向后兼容性时。但我想知道,只是为了提高效率,如果这是为什么停止评估任何事情,如果不应该记录某些消息(在示例中的调试之后)?在严重性操纵者之后做出一切“消失”?

刚才我有以下代码:

template <typename Type>
Logger & Logger::operator<<(const Type & message)
{
    if(this->get_message_level() <= this->get_severity())
    {
        BOOST_FOREACH(std::ostream* stream, this->_sinks)
        {
            *stream << message;
        }
    }
    return *this;
}

Logger & Logger::operator<< (Logger & (*pf)(Logger&))
{
    return pf(*this);
}

Logger & debug(Logger& logger)
{
    logger.lock();
    logger.set_severity(7);
    //...
    return logger;
}

Logger & end(Logger& logger)
{
    logger << std::endl;
    logger.unlock();
    return logger;
}

提前致谢。

4 个答案:

答案 0 :(得分:4)

你可以做一些简单的事情

extern "C" bool want_log;
#define LOG(Arg) do { if (want_log) \
   cout << __FILE__ << ":" << __LINE__ ": " << Arg << endl; } while(0)

并将其用作LOG("x=" << x)

答案 1 :(得分:1)

这可能有点棘手,取决于你愿意做出什么样的妥协 在语法中接受。如果你真的想要支持那一切 输出到ostream确实,那么你可以做到最好(就我而言) know)是一个包装类,类似于:

class Logger
{
    std::ostream* myDest;
public:
    //  Appropriate constructors...
    template<typename T>
    Logger& operator<<( T const& obj )
    {
        if ( myDest != NULL )
            (*myDest) << obj;
        return *this;
    }
};

如果关闭日志记录(Logger::myDest == NULL),则不会 转换代码将执行,但您仍将评估每个 参数。根据我的经验,这通常不是一个问题,因为大多数情况下 参数是字符串文字或简单变量,但是 这不是一个总共0的成本。它也有潜在的缺点 传播类型 std::ostream&(虽然我从未发现过 这在实践中是个问题。)

一个有点棘手的解决方案是使用以下几行的宏:

#define logger loggerInstance != NULL && (*loggerInstance)

这仍然允许大多数实际使用具有相同的语法 (因为&&运算符的优先级非常低),但可能会失败 有人试图过于聪明并嵌入日志记录的情况 以更复杂的表达式输出。除了不做 转换时,如果转换日志记录,则甚至不会评估参数 关闭。

最后,如果您接受不同的语法,您可以编写如下内容:

#ifndef NDEBUG
#define LOG(argList) logger << argList
#else
#define LOG(argList)
#endif

这要求用户编写LOG("x = " << x)而不是log << "x = " << x,如果要打开登录,则需要重新编译, 但它是我所知道的唯一解决方案,如果记录,它具有绝对的0成本 被关闭了。

根据我的经验,大多数应用程序都可以支持第一个解决方案;在一个 极少数情况下,你可能想要使用第二种;我从来没有见过 性能要求第三的应用。

请注意,即使使用第一个,您也可能想要使用宏 获取记录器实例,以便自动插入__FILE____LINE__,而在第二个,你可能仍然想要使用 包装类,以确保析构函数中的刷新;如果 应用程序是多线程的,在所有情况下你都需要包装器 为了使整个输出序列成为原子。

答案 2 :(得分:0)

您可以在Logger & Logger::operator<< (Logger & (*pf)(Logger&))运算符中检查严重性,只返回一个在这种情况下不打印任何内容的“空”记录器:

class EmptyLogger : public Logger {
  template <typename Type>
  Logger & Logger::operator<<(const Type & message)  { return *this; } 
};

EmptyLogger empty;  // Can be global/static, it won't cause any race conditions as it does nothing

Logger & Logger::operator<< (Logger & (*pf)(Logger&))
{
    if (logger.get_severity() < 5)  // Replace with a proper condition
      return empty;
    return pf(*this);  // What does pf() do? Aren't you returning reference to a temporary object?
}

答案 3 :(得分:0)

看看dr dobbs中关于登录c ++的这篇文章:

http://drdobbs.com/cpp/201804215?pgno=2

此页面解决了您特别关注的问题,但我建议您阅读整篇文章。

我基于这篇文章实现了一个日志系统,并且非常满意。