C++风格的记录器,支持__LINE_宏和其他
C++ style Logger that supports __LINE__ macro and others
我想制作一个可以像std::cout
一样使用的Logger,但我想记录一些额外的数据,如日期、时间、__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 /
所以实际上我需要从使用std::endl
操纵器的类中删除operator<<
,并用其他方法解决它,但如何做到呢?我正在考虑将logendl
宏中的std::endl
更改为其他自定义操纵器,然后这个自定义操纵器将完成实际执行operator<<
的工作,但我不知道如何完成。我正在寻找其他解决方案,有什么建议吗?
我是这么做的。这有点回避你的问题。也就是说,它不必定义endl
。我所做的是从构建消息的LogMessage
类中分离出一个Logger
类(它只接受字符串并输出到需要它们的地方)。
好处是:
-
每个类都非常简单。
-
非常简单的宏。我没有在下面定义宏,但它很容易做到。
-
无需定义
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;
}
您可能有一个带有LoggerAt(const char*filename, int lineno)
构造函数的LoggerAt
类(可能是std::ostringstream
的子类,等等),然后定义
#define LOG(Out) do {LoggerAt(__FILE__,__LINE__)
<< Out << std::endl; }while(0)
在我的一些C++项目中,我已经编码了:
void mom_inform_at(const char*fil, int lin, std::ostringstream& out)
{ out.flush();
std::clog << fil << ":" << lin
<< " INFORM: " << out.str() << std::endl ;
}
#define MOM_INFORM_AT(Fil,Lin,Output) do {
std::ostringstream out_##Lin;
out_##Lin << mom_outlog << Output ;
mom_inform_at(Fil,Lin,out_##Lin);
} while(0)
#define MOM_INFORM_AT_BIS(Fil,Lin,Output)
MOM_INFORM_AT(Fil,Lin,Output)
#define MOM_INFORM(Out)
MOM_INFORM_AT_BIS(__FILE__,__LINE__,Out)
使用类似MOM_INFORM("x=" << " point:" << pt);
的东西,您可以想象具有适当std::ostream& operator << (std::ostream&out, const Point&point)
函数的常见Point pt;
示例。
请注意,要方便地使用__FILE__
和__LINE__
,您最好使用宏。
我已经解决了自己的问题。这里发布的其他答案可能比main更好,但我想以一种简单的方式使用logger,就像在C++中使用std::cout
一样。此外,我的解决方案可能不是最佳的,可能会导致其他问题,但它符合我的要求。
我添加了一个自定义std::ostream
class CustomOstream : public std::ostream
{
public:
static CustomOstream& endl( CustomOstream& out )
{
return out;
}
};
并将宏更改为使用CustomOstream
中的endl
函数
#define __FILENAME__ (strrchr(__FILE__,'/') ? strrchr(__FILE__,'/') + 1 : __FILE__)
#define logendl
((ToolLogger::fileName = __FILENAME__).empty() ? "" : "")
<< ((ToolLogger::line = __LINE__) ? "" : "")
<< ((ToolLogger::function = __func__).empty() ? "" : "")
<< ToolLogger::CustomOstream::endl
此外,主类的operator<<
已更改
ToolLogger& operator<< (CustomOstream& (*f)(CustomOstream&))
{
// do something //
return *this;
}
现在记录器可以像我想要的一样使用
log << "some data" << logendl; // correct //
log << "some data" << std::endl; // won't compile -> correct //
- 在执行其他功能的同时播放动画(LED矩阵和Arduino/ESP8266)
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- GL_SHADERSTORAGE_BUFFER位置是否与其他着色器位置冲突
- 为什么我不能在 C++ 中的特定函数重载中调用同一函数的任何其他重载?
- 在其他文件中创建类时在 c++ 项目中不起作用
- 类与私有变量的其他类之间的线程安全性
- 将--whole archive链接器选项与CMake和具有其他库依赖项的库一起使用
- GlobalAlloc而不是其他分配方法
- C++从其他 constexpr 创建 lambda 不能按顺序执行 Constexpr
- 断言中的Fold表达式在某些计算机上编译,但在其他计算机上不编译
- Visual Studio(或任何其他工具)能否将地址解释为调用堆栈(boost上下文)的开头
- 结构和双指针隐藏在其他结构中,多层混淆
- UE4在OnComponentBeginOverlap上铸造其他actor
- 当我在其中一个线程执行中(在activemq-cpp中)捕获到特定值时,我如何终止/停止所有其他线程
- MESI协议和std::atomic-它是否确保所有写入立即对其他线程可见?
- 保留对其他类的成员函数的引用
- 为什么将值返回函数传递给重载=运算符对运算符函数有效,而对其他运算符无效
- 具有包含其他对象的类的对象创建顺序
- 让bool方法返回其他整数