我有一个 C++ 类,在其中放置了许多 std::cout 语句来打印有关该类正在处理的大量信号的信息性文本消息。我的目的是将这些文本消息重定向到名为 log 的函数。在此函数中,我有一个名为 mVerbose 的标志,它定义是否应打印日志文本。该函数的内容如下:
void XXXProxy::log(std::stringstream& ss)
{
if(mVerbose)
{
std::cout << ss;
ss << "";
}
}
那么,该函数的调用者代码片段如下: std::stringstream logStr;
logStr << "SE"
<< getAddr().toString()
<< ": WAITING on epoll..."
<< std::endl;
log(logStr);
我想超载<< operator in my XXXProxy in a way that I can get rid of creating a std::stringstream object and calling the log function. I want to be able to log the text messages as below and let the << operator aggregate everything into:
<< "SE"
<< getAddr().toString()
<< ": WAITING on epoll..."
<< std::endl;
所以我想要一个会员<< function that looks like:
void XXXProxy::operator << (std::stringstream& ss)
{
if(mVerbose)
{
std::cout << ss;
ss << "";
}
}
问题
我是一个相对新手的 C++ 开发人员,在尝试编写上述内容时遇到很多编译错误,如 << operator. Could you please make some suggestions or direct me to some links for me to correctly implement this << operator. Thanks.
如果您不想直接使用
std::cout
并且想要拥有自己的 Log 类,您可以实现一个简单的包装器,提供与 std::ostream
相同的接口: operator<<:
class Log {
private:
std::ostream& _out_stream;
//Constructor: User provides custom output stream, or uses default (std::cout).
public: Log(std::ostream& stream = std::cout): _out_stream(stream) {}
//Implicit conversion to std::ostream
operator std::ostream() {
return _out_stream;
}
//Templated operator>> that uses the std::ostream: Everything that has defined
//an operator<< for the std::ostream (Everithing "printable" with std::cout
//and its colleages) can use this function.
template<typename T>
Log& operator<< (const T& data)
{
_out_stream << data;
return *this;
}
}
因此,如果您为您的类实现
std::ostream& operator>>(std::ostream& os , const YourClass& object)
,则可以使用此 Log 类。
这种方法的优点是,您可以使用相同的机制来使
std::cout << your_class_object
工作,并使类与 Log 一起工作。
struct Foo
{
int x = 0; //You marked your question as C++11, so in class initializers
//are allowed.
//std::ostream::operator<< overload for Foo:
friend std::ostream& operator<<(std::ostream& os , const Foo& foo)
{
return os << foo.x;
}
};
int main()
{
Log my_log;
Foo my_foo;
my_foo.x = 31415;
my_log << my_foo << std::endl; //This prints "31415" using std::cout.
}
extern const
,并让该类实现 singleton。这允许您在程序中的任何位置访问日志。Log output (17:57): log message
。为此,您可以使用 std::endl
作为哨兵并存储一个标志,指示下一个输出何时为行的开头(日志消息的开头)。 查看下一个答案以获得完整且有效的实施。示例的时间戳只是一个示例:)。
但是如果您喜欢的话,我们可以尝试实现它。感谢 C++11 及其 STL 的重大改进,我们拥有了出色的时间/日期 API:std::chrono
std::chrono
基于三个方面:
此外,chrono 还提供三种类型的时钟:
std::system_clock
、std::steady_clock
和 std::high_resolution_clock
。在我们的例子中,我们使用 std::system_clock
(我们想要访问日期时间,而不是测量精确的时间间隔)。因此,如果我们必须为日志头实现时间戳,我们可以这样做:
编辑: 就像其他好东西一样,C++ 模板是很好的工具,除非你过度使用它。
我们的问题是
std::endl
是一个模板函数,所以我们不能将它直接传递给
另一个模板函数作为参数(在我们的例子中是operator<<
),因为编译器无法直接推导 std::endl 模板参数。这就是经常出现的错误“未解析的重载函数类型”。
但是有一种更简单的方法可以做到这一点:仅对
operator<<
使用 std::endl
的显式重载,并对其他所有内容使用其他模板:
class Log
{
private:
std::ostream& _out_stream;
bool _next_is_begin;
const std::string _log_header;
using endl_type = decltype( std::endl ); //This is the key: std::endl is a template function, and this is the signature of that function (For std::ostream).
public:
static const std::string default_log_header;
//Constructor: User passes a custom log header and output stream, or uses defaults.
Log(const std::string& log_header = default_log_header , std::ostream& out_stream = std::cout) : _log_header( log_header ) , _out_stream( out_stream ) , _next_is_begin( true ) {}
//Overload for std::endl only:
Log& operator<<(endl_type endl)
{
_next_is_begin = true;
_out_stream << endl;
return *this;
}
//Overload for anything else:
template<typename T>
Log& operator<< (const T& data)
{
auto now = std::chrono::system_clock::now();
auto now_time_t = std::chrono::system_clock::to_time_t( now ); //Uhhg, C APIs...
auto now_tm = std::localtime( &now_time_t ); //More uhhg, C style...
if( _next_is_begin )
_out_stream << _log_header << "(" << now_tm->tm_hour << ":" << now_tm->tm_min << ":" << now_tm->tm_sec << "): " << data;
else
_out_stream << data;
_next_is_begin = false;
return *this;
}
};
const std::string Log::default_log_header = "Log entry";
这段代码运行完美。我已将完整的实现推送到我的 github 帐户。
参考: