我想创建一个用于记录目的的类,其行为类似std::cout
,但会自动向流中插入其他信息。
我想要的示例用法类似于(暂时不关心对象和上下文类型,只是假设它们是std :: string):
Logger l;
l << "Event with object : " << obj << " while in context : " << context;
然后输出为:
[timestamp] Event with object : [obj_desc] while in context : [this context][eol][flush]
我一直在尝试:
template<typename T>
Logger& operator << (const T& msg){
std::cout << timestamp() << msg << std::endl << std::flush;
return *this;
}
但似乎std::cout
无法解析typename T
并且在segfaulting时无法输出std :: string。
一种可能的解决方案是为所有类型重载这个,但这相当烦人且耗时。
有更好的选项可以用更多信息来装饰std::cout
输出吗?
编辑:
我现在意识到endl和flush将被附加到每个消息的目的,但我仍然对一般的想法感兴趣。我更关心monadic语法附加任意数量的消息而不是<<
重载
答案 0 :(得分:0)
您的代码无效的原因是您没有为要传递给它的所有内容实现operator<<
。
本声明:
Logger l;
l << "Event with object : " << obj << " while in context : " << context;
基本上是这样做的(假设operator<<
是Logger
的成员,你的实现意味着它是):
Logger l;
l.operator<<("Event with object : ").operator<<(obj).operaator<<(" while in context : ").operator<<(context);
因此,对于字符串,对象,上下文等,您需要单独的operator<<
重载。您需要一种方法来指示何时将完整的日志消息刷新到std::cout
。
我会建议更像这样的事情:
struct LoggerStream
{
std::ostringstream strm;
struct Timestamp
{
};
~LoggerStream()
{
std::string s = strm.str();
if (!s.empty())
std::cout << s << std::flush;
}
LoggerStream& operator<< (const Timestamp &t)
{
strm << "[timestamp] "; // format this however you need
return *this;
}
LoggerStream& operator<< (const object &obj)
{
strm << "[obj_desc]"; // format this however you need
return *this;
}
LoggerStream& operator<< (const context &ctx)
{
strm << "[this context]"; // format this however you need
return *this;
}
LoggerStream& operator<< (std::ostream&(*f)(std::ostream&))
{
if (f == (std::basic_ostream<char>& (*)(std::basic_ostream<char>&)) &std::flush)
{
std::string s = strm.str();
if (!s.empty())
std::cout << s << std::flush;
strm.str("");
strm.clear();
}
else
strm << f;
return *this;
}
template<typename T>
LoggerStream& operator<< (const T& value)
{
strm << value;
return *this;
}
};
class Logger
{
LoggerStream getStream()
{
LoggerStream strm;
strm << Timestamp;
return strm;
}
};
然后你可以这样做:
Logger l;
l.getStream() << "Event with object : " << obj << " while in context : " << context;
...
l.getStream() << "Event with object : " << obj << " while in context : " << context;
...
Logger l;
LoggerStream strm = l.getStream();
strm << "Event with object : " << obj << " while in context : " << context << std::flush;
...
strm << Logger::Timestamp << "Event with object : " << obj << " while in context : " << context << std::flush;
...
可替换地:
struct Logger
{
std::ostringstream strm;
~Logger()
{
std::string s = strm.str();
if (!s.empty())
std::cout << "[timestamp] " << s << std::flush;
}
Logger& operator<< (const object &obj)
{
strm << "[obj_desc]"; // format this however you need
return *this;
}
Logger& operator<< (const context &ctx)
{
strm << "[this context]"; // format this however you need
return *this;
}
Logger& operator<< (std::ostream&(*f)(std::ostream&))
{
if (f == (std::basic_ostream<char>& (*)(std::basic_ostream<char>&)) &std::flush)
{
std::string s = strm.str();
if (!s.empty())
std::cout << "[timestamp] " << s << std::flush;
strm.str("");
strm.clear();
}
else
strm << f;
return *this;
}
template<typename T>
Logger& operator<< (const T& value)
{
strm << value;
return *this;
}
};
Logger() << "Event with object : " << obj << " while in context : " << context;
...
Logger() << "Event with object : " << obj << " while in context : " << context;
...
Logger l;
l << "Event with object : " << obj << " while in context : " << context << std::flush;
...
l << "Event with object : " << obj << " while in context : " << context << std::flush;
...
答案 1 :(得分:0)
如果需要,你当然可以重载流类,为你想要支持的所有数据类型提供operator<<
(这可能是“正确的”方式),但是,如果您只是追求的是将记录添加到常规流的快速方法,有一种更简单的方法:
#include <iostream>
#include <iomanip>
#include <sstream>
#include <ctime>
#include <unistd.h>
#define logcout std::cout << timestamp()
std::string timestamp(void) {
time_t now = time(0);
struct tm *tmx = localtime(&now);
std::ostringstream oss;
oss << '['
<< (tmx->tm_year+1900)
<< '-'
<< std::setfill('0') << std::setw(2) << (tmx->tm_mon+1)
<< '-'
<< std::setfill('0') << std::setw(2) << (tmx->tm_mday)
<< ' '
<< std::setfill('0') << std::setw(2) << (tmx->tm_hour)
<< ':'
<< std::setfill('0') << std::setw(2) << (tmx->tm_min)
<< ':'
<< std::setfill('0') << std::setw(2) << (tmx->tm_sec)
<< "] ";
return oss.str();
}
int main (int argc, char *argv[]) {
logcout << "A slightly\n";
sleep (5);
logcout << "sneaky" << " solution\n";
return 0;
}
输出:
[2015-05-26 13:37:04] A slightly
[2015-05-26 13:37:09] sneaky solution
不要被代码的大小所欺骗,我只提供了一个完整的可编译样本进行测试。问题的关键是单行:
#define logcout std::cout << timestamp()
然后您可以使用logcout
而不是std::cout
,并且每次出现都会在流内容前面加上一个任意字符串(在这种情况下,时间戳代表了大部分代码)。< / p>
这不是我称之为纯 C ++代码的东西,但是,如果你的需求基本上就是你所说的,那肯定会有所帮助。