【问题标题】:how do I write a logger class with cout style interface (logger << "Error: " << val << endl;)如何编写具有 cout 样式接口的记录器类 (logger << "Error: " << val << endl;)
【发布时间】:2011-01-15 04:29:24
【问题描述】:

我想创建一个记录器类,使其具有如下功能:

Logger log;
log << "Error: " << value << "seen" << endl;

这应该给我打印一条自定义格式的消息。例如。 "12-09-2009 11:22:33 看到错误 5"

我的简单类目前看起来像这样:

class Logger {
    private:
        ostringstream oss;
    public:
        template <typename T>
        Logger& operator<<(T a);
}

template <typename T>
Logger& Logger::operator<<(T a) {
    oss << a;
    return *this;
}

void functionTest(void) {
    Logger log;
    log << "Error: " << 5 << " seen";
}

这将导致 oss 正确地拥有缓冲区 "Error: 5 seen"。但我不知道我需要编写/修改什么其他功能才能在屏幕上打印一些东西。 有谁知道如何让它工作,或者是否有另一种方法来设计这个类以使我的功能正常工作?

【问题讨论】:

  • 问题:时间戳。您是否想要时间戳记:1)每个表达式 2)仅在每行的开头。您是否希望线路自行终止(根据您的 functionTest())。基本上,您需要更具体地了解添加时间戳的条件。另外,您是否都在控制台上登录文件?为什么需要一个特殊的类,为什么不能使用标准流?
  • Martin,这只是一个示例类。我剥离了原始的记录器类,只考虑了记录器的 cout 样式使用问题。

标签: c++ operators operator-overloading logging cout


【解决方案1】:

每个std::ostream 后面都有一个streambuf。它可以通过std::stream::rdbuf() 检索和设置。特别是,它可以被包装——您可以提供一个对流式文本进行后处理的 streambuf 对象。 (后处理意味着你无法区分 std::cout &lt;&lt; 123;std::cout &lt;&lt; "123";

在您的特定情况下,后处理相当简单。在每一行的开头你要插入一些字节。这仅仅意味着您应该跟踪您是否已经输出了当前行的前缀。如果没有,请这样做并设置标志。每当您看到换行符时,请重置它。您的 streambuf 包装器只有一个 bool 的状态。

【讨论】:

    【解决方案2】:

    据我所知,您的记录器与 ostringstream 没有什么不同。它只接受给定的内容并将其输出到字符串流。如果你想这样使用它,你可以为 Logger 编写一个析构函数,将字符串输出到 cout。

    Logger::~Logger()
    {
        std::cout<<getcurrentDateTimeAsString()<<" "<<oss.str()<<std::endl;
    }
    

    当然,如果在整个程序中创建和使用 Logger*,这将毫无意义。

    【讨论】:

      【解决方案3】:

      查看Compile time optimization - removing debug prints from release binaries 中提出的简单记录器。应该足以满足您的需求。 溴, 格拉詹

      【讨论】:

        【解决方案4】:

        问题是选择何时以及如何按行同步信息?所以无论是否缓冲,都只能控制EOL和线路上的信息——刷新或直接输出。

        即使将析构函数用作 EOL/Flush,

        { log &lt;&lt; [anything]; } 作为 inline-local 堆栈括号语法来调用退出括号的 log 的析构函数,或者作为 std::endl,都必须使用。

        除非使用诸如 '

        【讨论】:

          【解决方案5】:

          这(来自this post)做你想做的事,但它迫使你用 std::endl: 结束每一行

          class Logger {
              private:
                  ostringstream oss;
              public:
                  template <typename T>
                  Logger& operator<<(T a);
          
              Logger& operator<<( std::ostream&(*f)(std::ostream&) )
              {
                  if( f == std::endl )
                  {
                      std::cout << "12-09-2009 11:22:33" << oss.str() << std::endl;   
                      oss.str("");
                  }
                  return *this;
              }
          };
          
          template <typename T>
          Logger& Logger::operator<<(T a) {
              oss << a;
              return *this;
          }
          
          void functionTest(void) {
              Logger log;
              log << "Error: " << 5 << " seen" << std::endl;
          }
          
          int main()
          {
              functionTest();
          }
          

          编辑:根据您的评论,这似乎不是您想要的。那么我建议你按照 MSalters 所说的去做。

          【讨论】:

          • 输出std::endl时的内容转储后,应该清空oss的内容,以免下一行重复输出。
          • 不,我也想到了这个。但我不想强迫用户总是使用 endl。 cout 如何知道它何时有权打印?它不需要来自用户的 endl。
          • @David - 谢谢,已修复 @Dheeraj - cout 会在它们到达时打印它们(缓冲问题除外)。
          • @Dheeraj Agrawal:ostreams 在“刷新”时写入内容,这可以通过将 std::flush 传递给流、在流上调用 std::flush 或顺便传递 @987654327 时完成@ 给他们(传递std::endl 的行为定义为等价于传递"\n" 后跟std::flush
          • 您似乎正在放弃标准机械手!您不想将它们应用到流吗?
          猜你喜欢
          • 2017-05-13
          • 2011-02-22
          • 1970-01-01
          • 1970-01-01
          • 2020-03-19
          • 1970-01-01
          • 1970-01-01
          • 2020-01-10
          相关资源
          最近更新 更多