我想制作一个可以像std::cout
一样使用的记录器,但我想记录一些额外的数据,如日期、时间、__LINE__
, __func__
,和__FILE__
它应该自动保存到文件中。
示例
ToolLogger log;
log << "some data" << std::endl;
预期输出
[14.11.2015 21:10:12.344 (main.cpp) (main,14): some data
解决方案不充分
为此,我必须放置类似 __LINE__
的宏直接在我调用记录器的行中,否则宏将无法正常工作。我发现我可以替换 std::endl
用我的宏可以像这样实现这个黑魔法:
#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__
稍后需要。所以实际使用记录器将如下所示:
ToolLogger log;
log << "some data" << logendl;
在类里面我必须重载operator<<
为了让它发挥作用,我需要其中两个。一种用于取正常值,如 std::string
或int
,另一个取std::endl
操纵器。以下是我的类里面最重要的事情:
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;
问题
此解决方案的问题是我可以通过两种方式使用记录器:
log << "some data" << logendl; // correct //
log << "some data" << std::endl; // compiles -> wrong /
所以实际上我需要删除 operator<<
从我的类(class)中,需要 std::endl
操纵器,还有其他办法解决,但是怎么办呢?我正在考虑改变std::endl
在logendl
宏到其他自定义操纵器,然后该自定义操纵器将执行实际执行 operator<<
的工作,但我不知道该怎么做。我正在寻找其他解决方案,有什么建议吗?
最佳答案
这就是我所做的。这有点回避你的问题。也就是说,无需定义 endl
。我所做的就是从构建消息的 LogMessage 类中分离出一个 Logger 类(它只接受字符串并输出到您需要它们的任何地方)。 p>
好处是:
每个类本身都非常简单。
非常简单的宏。我没有定义下面的宏,但它很容易做到。
无需定义
endl
。当 LogMessage 类解构时,消息以分号结束
让我知道你的想法:
#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++ 风格记录器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33712798/