我有一个工作记录器类,它将一些文本输出到 Richtextbox(Win32,C++)中。 问题是,我总是这样使用它:
stringstream ss;
ss << someInt << someString;
debugLogger.log(ss.str());
相反,像流一样使用它会更方便,如下所示:
debugLogger << someInt << someString;
有没有比将所有内容转发到内部 stringstream 实例更好的方法?如果这样做,我什么时候需要冲洗?
您需要为您的班级适当实施
operator <<
。一般模式如下所示:
template <typename T>
logger& operator <<(logger& log, T const& value) {
log.your_stringstream << value;
return log;
}
请注意,这涉及(非
const
)引用,因为该操作会修改您的记录器。另请注意,您需要返回 log
参数才能使链接正常工作:
log << 1 << 2 << endl;
// is the same as:
((log << 1) << 2) << endl;
如果最里面的操作没有返回当前的
log
实例,所有其他操作要么在编译时失败(错误的方法签名),要么在运行时被吞没。
重载插入运算符<< is not the way to go. You will have to add overloads for all the endl or any other user defined functions.
方法是定义自己的streambuf,并将其绑定到流中。然后,您只需使用流即可。
以下是一些简单的例子:
正如 Luc Hermitte 指出的那样,有一篇 “Logging In C++” 文章描述了解决此问题的非常巧妙的方法。简而言之,假设您有一个如下所示的函数:
void LogFunction(const std::string& str) {
// write to socket, file, console, e.t.c
std::cout << str << std::endl;
}
可以编写一个包装器以在 std::cout 中使用它,就像这样:
#include <sstream>
#include <functional>
#define LOG(loggingFuntion) \
Log(loggingFuntion).GetStream()
class Log {
using LogFunctionType = std::function<void(const std::string&)>;
public:
explicit Log(LogFunctionType logFunction) : m_logFunction(std::move(logFunction)) { }
std::ostringstream& GetStream() { return m_stringStream; }
~Log() { m_logFunction(m_stringStream.str()); }
private:
std::ostringstream m_stringStream;
LogFunctionType m_logFunction;
};
int main() {
LOG(LogFunction) << "some string " << 5 << " smth";
}
(
在线演示)
此外,Stewart 提供了非常好的解决方案。
#include <string>
#include <memory>
#include <sstream>
#include <iostream>
class Logger
{
using Stream = std::ostringstream;
using Buffer_p = std::unique_ptr<Stream, std::function<void(Stream*)>>;
public:
void log(const std::string& cmd) {
std::cout << "INFO: " << cmd << std::endl;
}
Buffer_p log() {
return Buffer_p(new Stream, [&](Stream* st) {
log(st->str());
});
}
};
#define LOG(instance) *(instance.log())
int main()
{
Logger logger;
LOG(logger) << "e.g. Log a number: " << 3;
return 0;
}
std::ostringstream
扩展到登录 destruct 的一个,然后将其用作“临时”(不要给它命名,所以它会立即破坏):
class StreamToLog : public std::ostringstream
{
int level = INFO;
public:
StreamToLog(int level);
~StreamToLog();
};
StreamToLog::StreamToLog(int level) :
level(level)
{
}
StreamToLog::~StreamToLog()
{
const std::string s = str();
// This 'cout' is simply for demo purposes. You can do anything else
// like invoke a standard logger.
std::cout << levelToString(level) << " " << s << std::endl;
}
然后你可以在某处实现一些函数来检索该流:
StreamToLog Logger::log(int level)
{
return StreamToLog(level);
}
然后记录如下:
Logger::log(DEBUG) << "Hello, I'll have " << 3 " beers.";