使用 std::stream 接口创建一个日志记录对象

Create a logging object with std::stream interface

在内部,我们有一个接口 OurLog(const char *) 的日志记录功能。我希望能够通过类似于 std::ostringstream 的界面来使用它。换句话说,我希望有一个适配器对象,这样我就可以写:

 logging_class log;
 log << "There are " << num_lights << " lights\n";

并根据需要调用 OurLog() 将消息写入日志。

看起来从 std::streambuf 派生缓冲区 class 是正确的方法;怎么办呢?需要实现哪些功能?

如果你想要每一行

log << "There are " << num_lights << " lights\n";

导致呼叫您的 OurLog(const char *) 那么这个玩具示例可能有助于开始:

struct toy_logger {
    std::stringstream data;
    ~toy_logger() { OurLog(data.c_str()); }
    template <typename T> operator<<(const T& t) { data << t; }
};

在使用上只有细微差别:

toy_logger() << "There are " << num_lights << " lights\n";
        //^ create temporary that will get its destructor called at the end of the line

在 libstdc++ 文档中找到一个简单示例 here

class LoggingBuffer : public std::streambuf {
protected:
    virtual int_type overflow (int_type c) {
        if (c != EOF) {
            char msg[2] = {static_cast<char>(c), 0};
            OurLog(msg);
        }
        return c;
    }
};

class Logger : public std::ostream {
    LoggingBuffer logging_buffer;
public:
    Logger() : logging_buffer(), std::ostream(&logging_buffer) {}

};

extern Logger log; //instantiated in a cpp file for global use

不幸的是,它看起来不是很高效,因为它需要为每个字符调用一个函数。有更有效的方法吗?

我最近做了类似的事情。根据您的要求(特别是当需要冲洗时),有一些不同的方法。

一个非常简单的方法是简单地继承std::stringstream(然后如果你需要一个更简单的工厂函数,然后利用复制elision/RVO)。这将在对象超出范围时写入。

class LogStream : public std::ostringstream
{
public:
  LogStream(){}

  ~LogStream()
  {
        log(str());
  }
};

使用 streambuf 方法,您的基本需求是覆盖 sync()(可能还有析构函数)。

  virtual int sync() override {
    ::log(str());
    str("");//empty buffer
    return 0;//success
  }

然后用你的 streambuf 实例化 std::ostream。希望对你有帮助。