[英]Overloading operator<< in C++ with a prefix
我正在使用C ++编写一个具有以下语法的logger类:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
它打印:字符串1 6.2
这就是我班上的样子:
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;
}
};
它工作正常,但我还想为每一行添加一个前缀(例如时间戳)。 所以当我写:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
我想要显示这样的东西:
11:59:12 a string 1 6.2
我想到了几个解决方案:
1.保存列表/流中存储的每个输入,并使用额外的功能进行打印,然后清除列表/流:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
log.logd(); // <- this prints and then clears the internal stream/list.
2.保存列表/流中存储的每个输入,并在检测到“新行”字符后打印所有内容。 然后清除内部流/列表。
这两种解决方案都很不错,但我更喜欢将它们作为最后的手段使用。
有没有其他/更好的方法来实现我想要的?
您需要为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_); }
};
实际代码会更复杂,但请尝试实现以下逻辑。
添加一个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;
}
更一般地说,您应该扫描嵌入换行的data
,并将时间也放在每个换行之后。
上面的伪代码掩盖了棘手的部分。 由于data
可以是任何类型,因此last_char(data)
(以及对嵌入式换行的输出的更一般扫描last_char(data)
并last_char(data)
。 实现它的一般方法可能是将data
写入std::stringstream
。 然后,您可以扫描此字符串以获取换行符,最后将该字符串输出到*m_pOutStream
。
从std::stringbuf
LoggerStringBuf
派生一个类,比如说LoggerStringBuf
,并在其构造函数中为它输出std::ofstream
。 覆盖虚拟std::stringbuf::sync()
方法以从基本std::stringbuf::str()
方法中检索std::string
,并在将其写入std::ofstream
时为其添加时间戳。 这样,每次LoggerStringBuf
对象因任何原因刷新到std::ofstream
时都会生成一个新的时间戳,无论是由std::endl
还是std::flush
显式,还是由析构函数隐式地生成。
然后让您的Logger
类派生自std::ostream
并使用LoggerStringBuf
对象初始化它。 然后,您可以将输入值传输到Logger
,它们将缓存在LoggerStringBuf
对象中,直到刷新到std::ofstream
。 此时您可以根据需要预先设置时间戳。
例如:
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.