[英]Overloading operator<< in C++ with a prefix
I'm working on a logger class in C++ that has the following syntax: 我正在使用C ++编写一个具有以下语法的logger类:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
And it prints: a string 1 6.2 它打印:字符串1 6.2
This is what my class looks like: 这就是我班上的样子:
class Logger
{
private:
unique_ptr<ofstream> m_pOutStream;
public:
Logger(std::string sFile) : m_pOutStream(new ofstream(sFile, std::ios::app))
{}
template<typename T>
Logger& operator<< (const T& data)
{
*m_pOutStream << data;
return *this;
}
};
It works fine, but I would also like to add a prefix to every line (eg a timestamp). 它工作正常,但我还想为每一行添加一个前缀(例如时间戳)。 So when I write:
所以当我写:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
I want something like this to be displayed: 我想要显示这样的东西:
11:59:12 a string 1 6.2
I have thought of a couple of solutions: 我想到了几个解决方案:
1.Keep every input stored in a list/stream and use an extra function to print and then clear the list/stream: 1.保存列表/流中存储的每个输入,并使用额外的功能进行打印,然后清除列表/流:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
log.logd(); // <- this prints and then clears the internal stream/list.
2.Keep every input stored in a list/stream and print everything after a "new line" character is detected. 2.保存列表/流中存储的每个输入,并在检测到“新行”字符后打印所有内容。 And clear the internal stream/list after that.
然后清除内部流/列表。
Both of these solutions are nice but I'd prefer to use them only as a last resort. 这两种解决方案都很不错,但我更喜欢将它们作为最后的手段使用。
Is there any other/better way to achieve what I want? 有没有其他/更好的方法来实现我想要的?
You need to introduce an additional wrapper class for Logger that knows whether the line is starting or being appended. 您需要为Logger引入一个额外的包装类,它知道该行是在启动还是被追加。
class Appender
{
Appender(Logger& logger) : os_(os) { }
Appender& operator <<(const T& x) { os_ << x; return *this; }
};
class Logger
{
Appender operator <<(const T& x) { os_ << timestamp() << x; return Appender(os_); }
};
The actual code will be more complicated, but try implementing the following logic. 实际代码会更复杂,但请尝试实现以下逻辑。
Add a member_variable bool last_char_was_newline
, and use it in the code like this: 添加一个member_variable
bool last_char_was_newline
,并在代码中使用它,如下所示:
template<typename T>
Logger& operator<< (const T& data)
{
if (last_char_was_newline) {
*m_pOutStream << current_time_string();
last_char_was_newline = false;
}
*m_pOutStream << data;
if (last_char(data) == '\n') {
last_char_was_newline = true;
}
return *this;
}
To be more general, you should scan data
for embedded newlines, and put the time after each of them as well. 更一般地说,您应该扫描嵌入换行的
data
,并将时间也放在每个换行之后。
The above pseudo-code is glossing over the tricky part. 上面的伪代码掩盖了棘手的部分。 Since
data
can be any type, last_char(data)
(and the more general scanning of the output for embedded newlines) is non-trivial. 由于
data
可以是任何类型,因此last_char(data)
(以及对嵌入式换行的输出的更一般扫描last_char(data)
并last_char(data)
。 A general way to implement it might be to write data
to a std::stringstream
. 实现它的一般方法可能是将
data
写入std::stringstream
。 Then you can scan this string for newlines, and finally output the string to *m_pOutStream
. 然后,您可以扫描此字符串以获取换行符,最后将该字符串输出到
*m_pOutStream
。
Derive a class from std::stringbuf
, say LoggerStringBuf
, and give it a reference to your output std::ofstream
in its constructor. 从
std::stringbuf
LoggerStringBuf
派生一个类,比如说LoggerStringBuf
,并在其构造函数中为它输出std::ofstream
。 Override the virtual std::stringbuf::sync()
method to retrieve a std::string
from the base std::stringbuf::str()
method and prefix it with a timestamp when writing it to the std::ofstream
. 覆盖虚拟
std::stringbuf::sync()
方法以从基本std::stringbuf::str()
方法中检索std::string
,并在将其写入std::ofstream
时为其添加时间戳。 This way you generate a new timestamp every time your LoggerStringBuf
object is flushed to the std::ofstream
for any reason, whether explicitly by std::endl
or std::flush
, or implicitly by its destructor. 这样,每次
LoggerStringBuf
对象因任何原因刷新到std::ofstream
时都会生成一个新的时间戳,无论是由std::endl
还是std::flush
显式,还是由析构函数隐式地生成。
Then have your Logger
class derive from std::ostream
and initialize it with a LoggerStringBuf
object. 然后让您的
Logger
类派生自std::ostream
并使用LoggerStringBuf
对象初始化它。 Then you can stream input values to your Logger
and they will be cached in your LoggerStringBuf
object until flushed to the std::ofstream
. 然后,您可以将输入值传输到
Logger
,它们将缓存在LoggerStringBuf
对象中,直到刷新到std::ofstream
。 At which time you can prepend timestamps as needed. 此时您可以根据需要预先设置时间戳。
For example: 例如:
class LoggerStringBuf : public std::stringbuf
{
private:
std::ostream &m_OutStream;
protected:
virtual int sync()
{
int ret = std::stringbuf::sync();
std::string s = str();
str("");
// note sure if the string includes non-flushing
// line breaks. If needed, you can use std::getline()
// to break up the string into multiple lines and
// write a timestamp for each line...
//
m_OutStream << "[timestamp] " << s << std::endl;
return ret;
};
public:
LoggerStringBuf(std::ostream &OutStream)
: std::stringbuf(std::ios_base::out), m_OutStream(OutStream)
{
}
~LoggerStringBuf()
{
sync();
}
};
class Logger : public std::ostream
{
private:
std::ofstream m_OutStream;
LoggerStringBuf m_Buf;
public:
Logger(const std::string &sFile)
: std::ostream(0), m_OutStream(sFile, std::ios::app), m_Buf(m_OutStream)
{
init(&m_Buf);
}
template<typename T>
std::ostream& operator<< (const T& data)
{
return static_cast<std::ostream&>(*this) << data;
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.