<<运算符重载C++用于日志记录目的
<< operator overloading in C++ for logging purposes
我有一个C++类,我在其中放置了许多std::cout语句,以打印有关该类正在处理的大量信号的信息性文本消息。我的意图是将这些文本消息重定向到一个名为log的函数。在这个函数中,我有一个名为mVerbose的标志,它定义了是否应该打印日志文本。此功能的内容如下:
void XXXProxy::log(std::stringstream& ss)
{
if(mVerbose)
{
std::cout << ss;
ss << "";
}
}
然后,此函数的调用方代码片段如下:std::字符串流logStr;
logStr << "SE"
<< getAddr().toString()
<< ": WAITING on epoll..."
<< std::endl;
log(logStr);
我想使<lt;运算符,这样我就可以摆脱创建std::stringstream对象和调用log函数的麻烦。我希望能够将文本消息记录如下,并让<lt;操作员将所有内容聚合为:
<< "SE"
<< getAddr().toString()
<< ": WAITING on epoll..."
<< std::endl;
所以我想要一个会员<lt;看起来像的函数
void XXXProxy::operator << (std::stringstream& ss)
{
if(mVerbose)
{
std::cout << ss;
ss << "";
}
}
问题
我是一个相对新手的C++开发人员,在尝试编写上面所说的<lt;操作人员你能为我提供一些建议或指导我访问一些链接吗<操作人员谢谢
如果你不想直接使用std::cout
,并且你想拥有自己的Log类,你可以实现一个简单的包装器,提供与std::ostream
相同的接口:operator<lt:
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;
}
}
因此,如果为类实现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)
{
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.
}
可能的改进:
- 您可以编写Log类的
extern const
,并使该类实现一个singleton。这允许您访问程序中的任何位置的日志 - 日志输出中通常有一个标头,如
Log output (17:57): log message
。要做到这一点,您可以使用std::endl
作为一个哨兵,并存储一个标志,说明下一个输出何时是一行的开头(日志消息的开头)检查下一个答案以获得完整且有效的实现
参考文献:
- std::ostream
- 运算符<lt;对于std::ostream
- std::enable_if
- std::is_same
- decltype说明符
示例的时间戳只是一个示例:)。
但如果你喜欢,我们可以尝试实现它。感谢C++11及其STL的巨大改进,我们有一个出色的时间/日期API:std::chrono
std::chrono
基于三个方面:
- 时钟
- 工期
- 时间点
此外,chrono提供三种类型的时钟,std::system_clock
、std::steady_clock
和std::high_resolution_clock
。在我们的例子中,我们使用std::system_clock
(我们想要访问日期时间,而不是测量精确的时间间隔)
欲了解更多关于std::chrono的信息,请查看Bo的youtube教程。
因此,如果我们必须为日志头实现一个时间戳,我们可以这样做:
编辑:与其他好东西一样,C++模板是很好的工具,除非你过度使用它。
我们的问题是std::endl
是一个模板函数,所以我们不能直接将它传递给将其他模板化函数作为parameter(在我们的例子中为operator<<
),因为编译器无法直接推导std::endl模板参数。这是一个反复出现的错误"未解析的重载函数类型"。
但有一种更简单的方法可以做到这一点:仅对std::endl
使用operator<<
的显式重载,并对其他所有内容使用其他模板:
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帐户。
参考:
- std::chrono
- std::chrono::system_clock
- std::chrono::system_clock::now()
- std::time_t
- std::chrono::system_clock::to_time_t()
- std::tm
- std::localtime()
- 请解释这句话(cout<<1+int((a<b)^((b-a)&1) )<<endl
- 呼叫运营商<<临时
- 将两个数组中的差异记录在第三个数组中
- 如何防止clang格式在流运算符调用之间添加换行符<<
- <<操作员在下面的行中工作
- 禁止在控制台上记录谷神星
- 创建 Spdlog 异步文件记录器时遇到困难
- fastrtps:如何在发布/订阅级别使用 DDS 历史记录?
- 如何将自定义记录器与websocketpp一起使用?
- C++结构到德尔福记录dll调用
- 如何判断SSL_read是否已经接收并处理了来自单个消息的所有记录
- 如何实现具有多个平台__FILE__和__LINE__信息的 C/C++ 可变参数日志记录宏?
- 我是否可以使用 win32 句柄以编程方式记录发送到/接收到 USB/COM 的内容
- 使用 Doxygen 记录枚举类值,而不启用EXTRACT_ALL
- 在C++中隐藏键盘记录器的控制台窗口
- 堆栈上的最大激活记录数
- 记录Doxygen中的命名
- 为什么Qt Creator的应用程序输出不能从spdlog记录器打印
- 在SQLITE数据库中写入记录需要花费大量时间.如何提高刀片操作效率?
- Qt 错误:QSqlQuery::value:尝试从表中检索统计信息时未定位在有效记录上 (QComboBox)