问题描述
我想制作一个可以像std::cout
这样使用的记录仪,但是我想记录一些额外的数据,例如日期,时间,__LINE__
,__func__
和__FILE__
,这些数据应该保存到文件中自动.
I want to make a Logger that can be used like std::cout
, but I want to log some extra data like date, time, __LINE__
, __func__
, and __FILE__
which should be saved to the file automatically.
ToolLogger log;
log << "some data" << std::endl;
预期产量
[14.11.2015 21:10:12.344 (main.cpp) (main,14): some data
解决方案不足
为此,我必须将像__LINE__
这样的宏直接放在调用记录器的行中,否则这些宏将无法正常工作.我发现我可以用自己的宏替换std::endl
,它将像下面这样处理黑魔法:
Inadequate solution
To do this I have to put macros like __LINE__
direct in the line where I call my logger, otherwise the macros won't work correct. I found that I can replace std::endl
with my macro that will do this black magic like this:
#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl \
((ToolLogger::fileName = __FILENAME__).empty() ? "" : "") \
<< ((ToolLogger::line = __LINE__) ? "" : "") \
<< ((ToolLogger::function = __func__).empty() ? "" : "") \
<< std::endl
宏logendl
使用我的ToolLogger
类中的静态变量来保存以后需要的__LINE__
,__func__
和__FILE__
的值.因此,实际上使用记录器将如下所示:
The macro logendl
uses static variables from my ToolLogger
class to save the values of __LINE__
, __func__
and __FILE__
needed later. So actually using the logger will looks like this:
ToolLogger log;
log << "some data" << logendl;
在课堂上,我必须重载operator<<
才能使它正常工作,我需要两个.一个用于获取正常值,例如std::string
或int
,另一个用于获取std::endl
机械手.这是我班上最重要的事情:
In the class i have to overload the operator<<
to get this to work, and I need two of them. One for taking the normal values like std::string
or int
, and the other to take the std::endl
manipulator. Here is the most important things from my class:
class ToolLogger
{
public:
// standard operator<< //
template<typename T>
ToolLogger& operator<< (const T& str)
{
out << str;
return *this;
}
// operator<< for taking the std::endl manipulator //
typedef std::basic_ostream<char, std::char_traits<char> > CoutType;
typedef CoutType& (*StandardEndLine)(CoutType&);
ToolLogger& operator<<(StandardEndLine manip)
{
// save fileName, line and function to the file //
// and all what is already in stringstream //
// clear stringstream //
return *this;
}
static string fileName;
static int line;
static string function;
private:
ofstream file;
std::stringstream out;
};
string ToolLogger::fileName;
int ToolLogger::line;
string ToolLogger::function;
问题
此解决方案中的问题是我可以通过两种方式使用记录器:
Problem
The problem in this solution is that I can use my logger in two ways:
log << "some data" << logendl; // correct //
log << "some data" << std::endl; // compiles -> wrong /
所以实际上我需要从使用std::endl
机械手的类中删除operator<<
,并以其他方式解决它,但是该怎么做呢?我当时正在考虑将logendl
宏中的std::endl
更改为其他自定义操纵器,然后此自定义操纵器将完成实际上正在执行operator<<
的工作,但是我不知道该怎么做.我在寻找其他解决方案,有什么建议吗?
So actually I need to remove the operator<<
from my class that takes std::endl
manipulator, and solve it other way, but how to do it? I was thinking about changing std::endl
in logendl
macro to other custom manipulator, and then this custom manipulator will do the work that is actually doing the operator<<
, but I have no idea how to do it. I'm looking for other solution, any suggestions?
推荐答案
这就是我要做的.这有点像你的问题.也就是说,无需定义endl
.我要做的是从LogMessage
类中分离出一个Logger
类(该类只接收字符串并输出,然后输出到需要它们去的任何地方).
Here's what I do. It kind of skirts your question. That is, is does away with having to define an endl
. What I do is separate out a Logger
class (which just takes strings and outputs then to wherever you need them to go) from a LogMessage
class which builds a message.
好处是:
-
每个类都非常简单.
Each class, on it's own, is pretty simple.
非常简单的宏.我没有在下面定义宏,但是它很容易做到.
Very simple macros. I don't define the macro below but it's easy enough to do.
无需定义endl
.当LogMessage类破坏时,消息以分号结束
No need to define an endl
. The message ends at the semicolon when the LogMessage class destructs
让我知道您的想法:
#include <iostream>
#include <sstream>
#include <string>
// logger class
// this is not complete, it exists just to illustrate the LogIt function
class Logger
{
public:
void LogIt(const std::string & s)
{
std::cout << s << std::endl;
}
};
// builds a logging message; outputs it in the destructor
class LogMessage
{
public:
// constructor
// takes identifying info of message. You can add log level if needed
LogMessage(const char * file, const char * function, int line)
{
os << file << ": " << function << '(' << line << ") ";
}
// output operator
template<typename T>
LogMessage & operator<<(const T & t)
{
os << t;
return *this;
}
// output message to Logger
~LogMessage()
{
Logger logger; // get logger here (perhaps it's a singleton?)
logger.LogIt(os.str());
}
private:
std::ostringstream os;
};
int main()
{
// example usage
// typically this is invoked via a simple macro to reduce typing of the LogMessage constructor
LogMessage(__FILE__, __func__, __LINE__) << "this is an int " << 5;
}
这篇关于支持__LINE__宏等的C ++样式记录器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!