重载运算符<<C++中带前缀
Overloading operator<< in C++ with a prefix
我正在C++中研究具有以下语法的记录器类:
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.将每个输入存储在列表/流中,并在检测到"换行符"字符后打印所有内容。然后清除内部流/列表。
这两种解决方案都很好,但我宁愿只将它们用作最后的手段。
有没有其他/更好的方法来实现我想要的?
从std::stringbuf
派生一个类,比如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;
}
};
您需要为 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)
(以及更一般的输出扫描以查找嵌入式换行符)并非易事。实现它的一般方法可能是将data
写入std::stringstream
。然后你可以扫描这个字符串的换行符,最后将字符串输出到*m_pOutStream
。
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 呼叫运营商<<临时
- 如何防止clang格式在流运算符调用之间添加换行符<<
- <<操作员在下面的行中工作
- 编译器如何在前缀和 postix 运算符之间进行区分?
- 查找带有 Anaconda cmake 前缀的 boost-python3
- 迭代器类的重载前缀增量运算符会引发分段错误
- 如何在友元函数中使用静态成员而不添加前缀 [类名]::
- 生成前缀位掩码
- 定义宏以将前缀 0x 添加到十六进制字符串文本
- CMake 错误 - 目标 foo INTERFACE_SOURCES属性包含在源目录中以前缀为前缀的路径
- 如何在自定义对象的<<运算符中添加自定义前缀
- 高效的字符串截断算法,按顺序删除相等的前缀和后缀
- 以C++显示单词的所有前缀
- QXmlStreamWriter,命名空间和前缀
- 为什么 C++ 程序员更喜欢前缀 ++,而 Java 程序员更喜欢后缀 ++?
- 为什么 libclang 会错误解析带有 .h 前缀C++标头?
- 目标是找到两个 c 字符串之间的公共前缀(必须使用特定的函数标头)
- 我需要在C++的两个字符串之间找到共同的前缀
- 更新 Visual Studio 2017,现在出现编译错误 C7510:"回调":使用依赖模板名称必须以 'template' 为前缀