使用 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。希望对你有帮助。
在内部,我们有一个接口 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。希望对你有帮助。