使用ostream C ++运算符进行惰性求值

时间:2011-02-17 23:07:49

标签: c++ logging iostream lazy-evaluation

我正在寻找一种可移植的方法来在C ++中为日志记录类实现延迟评估。 让我们说我有一个像

这样的简单记录功能
void syslog(int priority, const char *format, ...);

然后在syslog()函数中我们可以这样做:

if (priority < current_priority)
  return;

所以我们实际上从未调用格式化函数(sprintf)。 另一方面,如果我们使用像

这样的日志流
log << LOG_NOTICE << "test " << 123;

所有格式化始终都会执行,这可能需要很长时间。 有没有可能实际使用ostream的所有好东西(比如自定义&lt;&lt;操作符用于类,类型安全,优雅的语法...),以便在检查日志记录级别后执行格式化?

4 个答案:

答案 0 :(得分:4)

这看起来像可以使用表达式模板处理的东西。但要注意,表达模板实现起来非常重要。

它们如何工作的一般概念是操作符只是构建一个临时对象,并将该临时对象传递给日志对象。日志记录对象将查看日志记录级别并决定是执行临时对象中包含的操作,还是仅丢弃它。

答案 1 :(得分:4)

我在我们的应用程序中所做的是在日志记录级别过滤该语句的情况下返回boost::iostreams::null_stream。这工作得相当好,但仍会调用所有&lt;&lt;运算符。

如果在编译时设置了日志级别,则可以切换到null为&lt;&lt;的对象。操作

否则,正如Jerry所说,它是表达模板。

答案 2 :(得分:3)

最简单,最直接的方法是简单地将格式移到格式之外:

MyLogger log;  // Probably a global variable or similar.

if (log.notice())
  log << "notified!\n" << some_function("which takes forever to compute"
    " and which it is impossible to elide if the check is inside log's"
    " op<< or similar");

if (log.warn()) {
  log << "warned!\n";
  T x;
  longer_code_computing(value_for, x);  // easily separate out this logic
  log << x;
}

如果您真的想缩短常见情况,可以使用宏:

#define LOG_NOTICE(logger) if (logger.notice()) logger <<

LOG_NOTICE(log) << "foo\n";
// instead of:
if (log.notice()) log << "foo\n";

但节省的是微不足道的。

一个可能的MyLogger:

struct MyLogger {
  int priority_threshold;

  bool notice()  const { return notice_priority  < current_priority; }
  bool warn()    const { return warn_priority    < current_priority; }
  bool etc()     const { return etc_priority     < current_priority; }

  template<class T>
  MyLogger& operator<<(T const &x) {
    do_something_with(x);
    return *this;
  }
};

这里的问题是将iostream样式的运算符重载与类似printf的日志记录函数混合 - 特别是将操纵符和格式化标记/字段从iostreams转换为格式字符串。您可以写入一个字符串流,然后将其分块到您的系统日志功能,或尝试更好的东西。上面的MyLogger工作最简单,如果它还包含一个可以转发的ostream引用,但是你需要更多的操作&lt;&lt;如果你这样做,iomanips的重载(例如endl)。

答案 3 :(得分:2)

对于我的我做了一个debug_ostream类,它有模板化&lt;&lt;运营商。这些运算符在调用实数运算符之前检查调试级别。

您需要为const char*std::ostream& (*x)(std::ostream&)定义非模板覆盖,否则这些覆盖不起作用。我不确定为什么。

使用内联和足够高的优化级别,编译器将整个输出行转换为调试级别的单个检查,而不是每个输出项目一个。

我应该补充一点,这并不能解决原来的问题。例如,如果调试行的一部分是调用昂贵的函数来获取输出值,那么仍然会调用该函数。我的解决方案只会跳过格式化开销。