【问题标题】:Lazy evaluation with ostream C++ operators使用 ostream C++ 运算符进行惰性求值
【发布时间】:2011-06-29 11:33:12
【问题描述】:

我正在寻找一种可移植的方式来在 C++ 中为日志记录类实现惰性求值。 假设我有一个简单的日志记录功能,例如

void syslog(int priority, const char *format, ...);

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

if (priority < current_priority)
  return;

所以我们从未真正调用格式化函数 (sprintf)。 另一方面,如果我们使用类似的日志流

log << LOG_NOTICE << "test " << 123;

总是执行所有的格式化,这可能需要很多时间。 是否有可能以在检查日志记录级别后执行格式化的方式实际使用 ostream 的所有优点(例如自定义

【问题讨论】:

    标签: c++ logging iostream lazy-evaluation


    【解决方案1】:

    这看起来可以用表达式模板来处理。但是请注意,表达式模板的实现可能非常重要。

    它们如何工作的总体思路是,操作符只是构建一个临时对象,然后您将该临时对象传递给您的日志记录对象。日志对象会查看日志级别并决定是执行临时对象中包含的操作,还是直接丢弃它。

    【讨论】:

    • 表达式模板无法处理我的回答中的 some_function("which takes forever...") 情况。
    • 我已经考虑过了。困扰我的是,即使我返回一个“伪/空”流对象,表达式的其余部分仍然会被评估(
    【解决方案2】:

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

    如果日志级别是在编译时设置的,你可以切换到一个带有空

    否则,就是 Jerry 说的表达式模板。

    【讨论】:

    • 如果在编译时设置日志级别,我可以使用 log(LEVEL)
    【解决方案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 的日志记录功能混合在一起——特别是将操纵器和格式化标志/字段从 iostream 转换为格式字符串。您可以写入一个字符串流,然后将其分块到您的 syslog 函数中,或者尝试一些更高级的东西。如果上面的 MyLogger 还包含它可以转发的 ostream 引用,则它的工作最简单,但如果你这样做,你将需要更多的 op

    【讨论】:

    • 这似乎是目前最好的解决方案(如果我错了,请纠正我 - 与 Zan Lynx 的基本相同)。我将尝试实现这一点并发布我的结果。
    • @SavinG:不,但它是相似的。请参阅 Zan 的最后一段:“我应该补充一点,这并不能解决原始问题。例如,如果调试行的一部分是调用昂贵的函数来获取输出值,那么仍然会调用该函数。我的解决方案只跳过格式化开销。”
    【解决方案4】:

    我创建了一个 debug_ostream 类,它具有模板化的

    您需要为const char*std::ostream&amp; (*x)(std::ostream&amp;) 定义非模板覆盖,否则它们将不起作用。我不知道为什么。

    通过内联和足够高的优化级别,编译器会将整个输出行转换为对调试级别的一次检查,而不是每个输出项检查一次。

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

    【讨论】:

    • 您需要指向函数的重载(总共三个)来处理操纵器,但是 char const* 应该可以与通用模板化 op
    猜你喜欢
    • 2015-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 1970-01-01
    • 2018-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多