在C ++中__FILE __,_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

时间:2009-02-27 23:27:11

标签: c++ debugging logging c-preprocessor

假设您的C ++编译器支持它们,是否有任何特殊原因 使用__FILE____LINE____FUNCTION__进行日志记录和调试?< / p>

我主要关注的是为用户提供误导性数据 - 例如,由于优化而报告错误的行号或功能,或者因此导致性能下降。

基本上,我可以信任__FILE____LINE____FUNCTION__ 总是做正确的事吗?

6 个答案:

答案 0 :(得分:169)

__FUNCTION__是非标准的,C99 / C ++ 11中存在__func__。其他人(__LINE____FILE__)也没关系。

它将始终报告正确的文件和行(如果您选择使用__FUNCTION__ / __func__,则会显示功能)。优化是一个非因素,因为它是一个编译时宏扩展; 永远不会以任何方式影响效果。

答案 1 :(得分:36)

在极少数情况下,将__LINE__给出的行更改为其他内容会很有用。我已经看到GNU configure为某些测试报告了适当的行号,它在原始源文件中没有出现的行之间插入了一些伏都教。例如:

#line 100

将使以下行以__LINE__ 100开头。您可以选择添加新文件名

#line 100 "file.c"

它很少有用。但如果需要,我知道没有其他选择。实际上,也可以使用宏来代替行,这必须导致上述两种形式中的任何一种。使用boost预处理器库,可以将当前行增加50:

#line BOOST_PP_ADD(__LINE__, 50)

我认为提及它是有用的,因为您询问了__LINE____FILE__的使用情况。一个人从未从C ++中获得足够的惊喜:)

编辑: @Jonathan Leffler在评论中提供了一些更好的用例:

  

与#line混淆对于希望将用户C代码中报告的错误与用户的源文件保持一致的预处理器非常有用。 Yacc,Lex和(更多在我家)ESQL / C预处理器就是这样做的。

答案 2 :(得分:28)

仅供参考:g ++提供非标准的__PRETTY_FUNCTION__宏。直到现在我还不知道C99 __func__(谢谢Evan!)。我认为当它可用于额外的类范围时,我仍然更喜欢__PRETTY_FUNCTION__。

PS:

static string  getScopedClassMethod( string thePrettyFunction )
{
  size_t index = thePrettyFunction . find( "(" );
  if ( index == string::npos )
    return thePrettyFunction;  /* Degenerate case */

  thePrettyFunction . erase( index );

  index = thePrettyFunction . rfind( " " );
  if ( index == string::npos )
    return thePrettyFunction;  /* Degenerate case */

  thePrettyFunction . erase( 0, index + 1 );

  return thePrettyFunction;   /* The scoped class name. */
}

答案 3 :(得分:8)

C ++ 20 std::source_location

C ++最终添加了一个非宏选项,当C ++ 20广泛使用时,它将在将来的某个时候占主导地位。

文档说:

constexpr const char * function_name()const noexcept;

6返回:如果此对象表示函数主体中的位置, 返回实现定义的NTBS,该NTBS应该与 函数名称。否则,返回一个空字符串。

其中NTBS表示“空终止字节字符串”。

当支持到达GCC时,我会尝试一下,带有g++-9 -std=c++2a的GCC 9.1.0仍然不支持。

https://en.cppreference.com/w/cpp/utility/source_location的声明用法类似于:

#include <iostream>
#include <string_view>
#include <source_location>

void log(std::string_view message,
         const std::source_location& location std::source_location::current()
) {
    std::cout << "info:"
              << location.file_name() << ":"
              << location.line() << ":"
              << location.function_name() << " "
              << message << '\n';
}

int main() {
    log("Hello world!");
}

可能的输出:

info:main.cpp:16:main Hello world!

__PRETTY_FUNCTION__ vs __FUNCTION__ vs __func__ vs std::source_location::function_name

回答于:What's the difference between __PRETTY_FUNCTION__, __FUNCTION__, __func__?

答案 4 :(得分:7)

就个人而言,除了调试消息之外,我不愿意使用它们。我已经做到了,但我尽量不向客户或最终用户展示这类信息。我的客户不是工程师,有时不精通计算机。我可能会将此信息记录到控制台,但正如我所说,除了调试版本或内部工具之外,我不情愿。不过,我认为这取决于您拥有的客户群。

答案 5 :(得分:5)

我一直都在使用它们。我唯一担心的是在日志文件中赠送IP。如果你的功能名称非常好,你可能会更容易发现商业机密。这有点像带有调试符号的运输,只有更难找到的东西。在99.999%的情况下,没有什么不好的。