我有一个自定义记录器类,它具有用于调试、信息等消息的不同方法。除了日志消息,我还想打印调用记录器方法的文件的名称和行,以及时间戳。
我现在的解决方案是这样的:
m_logger.debug("some debug message", __FILE__ , __LINE__, __TIME__);
问题:有没有办法避免调用这两个宏__FILE__
,__LINE__
并__TIME__
实现某个人以便自动调用它?
谢谢。
也许是这样的:
#define debug_with_ft(x) debug(x, __FILE__, __TIME__)
...
m_logger.debug_with_ft("some debug message");
当然,有多种不同的方法可以解决这个问题,例如m_logger
在宏中隐藏整个:
#define debug_with_ft(x) m_logger.debug(x, __FILE__, __TIME__)
__TIME__
是一个预处理器宏,它将在编译时产生当前时间。因此,不应在您的情况下使用它,因为您可能希望在运行时访问当前时间(记录消息时)。
__FILE__
也是一个预处理器宏,必须在预编译时进行评估,您不能以其他方式实现它。
您可能还对__LINE__
宏感兴趣
这是宏是正确解决方案的一种情况。您想使用宏调用您的记录器(因为只有宏可以自动插入__FILE__
and __LINE__
),并且您希望以允许客户端代码传递一个或多个消息元素的方式执行此操作,可能使用<<
. 通常的做法是这样的:
#define LOG() m_logger.getStream( __FILE__, __LINE__ )
, 其中m_logger.getStream
插入标头(使用其参数和当前时间),并返回对日志流的引用,或也实现的特殊包装类— 仅<<
返回对流的引用是最简单的,但使用包装允许捕获消息的结尾(因为包装器是临时的,它将在完整表达式的末尾被破坏),释放一个锁getStream
(以便日志记录是线程安全的),或者确保输出是原子的,如果<<
包装是这样的:
template <typename T>
LogStream& LogStream::operator<<( T const& object )
{
if ( myStream != NULL ) {
*myStream << object;
}
}
,您可以禁用日志记录,如果禁用,则不会进行任何转换。
为什么你认为是坏的__FILE__
?__TIME__
我不确定它们本身是否真的是宏。它们是所谓的宏名称。我也认为它们是由标准定义的,因此使用它们应该是安全且可以的。
在旁注中,您似乎错过了编译时评估和运行时评估的概念。
在运行时如何知道源文件的名称?这就是为什么您应该使用__FILE__
,因为编译器将为您评估文件名。
相反__TIME__
,编译器也替换了宏名。您想记录编译或调用该行的时间吗?如果是后者,您应该使用适当的运行时函数。
我不认为有任何绕过的可能__FILE__
。
但是,如果您只想缩短通话时间
我使用以下代码段:
#ifndef NDEBUG
#define DEBUG_MSG(msg) do{ std::cerr << __FILE__ << "(@" << __LINE__ << "): " << msg << '\n'; } while( false )
#else
#define DEBUG_MSG(msg) do{ } while ( false )
#endif
我发现它非常便携且易于使用,但它并不意味着用作类方法。您可能可以将其定义为
#define DEBUG_MSG(msg) debug( msg, __FILE__, __LINE__ )
并使用
m_logger.DEBUG_MSG("message");
因此宏将扩展到设计的函数调用。然而,这是相当冒险的。如果您更改debug
签名,则需要更改宏,并且它很难移植。