我正在使用C ++编写具有以下语法的logger类:
v1.9.1
它打印: 字符串1 6.2
这就是我班上的样子:
Logger log("mylog.txt");
log << "a string" << " " << 1 << " " << 6.2 << "\n";
它工作正常,但我还想为每一行添加一个前缀(例如时间戳)。 所以当我写:
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";
我想到了几个解决方案:
1.保存列表/流中存储的每个输入,并使用额外的功能打印然后清除列表/流:
11:59:12 a string 1 6.2
2.保存列表/流中存储的每个输入,并在&#34;新行&#34;之后打印所有内容。检测到字符。然后清除内部流/列表。
这两种解决方案都很不错,但我更喜欢将它们作为最后的手段使用。
有没有其他/更好的方法来实现我想要的目标?
答案 0 :(得分:1)
您需要为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_); }
};
答案 1 :(得分:1)
实际代码会更复杂,但请尝试实现以下逻辑。
添加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)
(以及对嵌入换行的输出的更一般扫描)是非平凡的。实现它的一般方法可能是将data
写入std::stringstream
。然后,您可以扫描此字符串以获取换行符,最后将该字符串输出到*m_pOutStream
。
答案 2 :(得分:1)
从std::stringbuf
派生一个类,比如LoggerStringBuf
,并在其构造函数中为其输出std::ofstream
提供一个引用。覆盖虚拟std::stringbuf::sync()
方法以从基础std::string
方法检索std::stringbuf::str()
,并在将其写入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;
}
};