在 C + + 中使用 _ _ FILE _ _,_ _ LINE _ _ 和 _ _ FUNCION _ _ _

假设您的 C + + 编译器支持它们,那么 没有使用 __FILE____LINE____FUNCTION__进行日志记录和调试有什么特别的原因吗?

我主要关心的是向用户提供误导性的数据ーー例如,由于优化而报告错误的行号或函数ーー或者由于性能受到影响。

基本上,我可以信任 __FILE____LINE____FUNCTION__一直都是做正确的事情吗?

289988 次浏览

__FUNCTION__是非标准的,__func__存在于 C99/C + + 11中,其他的(__LINE____FILE__)都很好。

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

就个人而言,除了调试消息之外,我不愿意将它们用于其他任何用途。我已经这样做了,但我尽量不向客户或最终用户显示这种信息。我的客户不是工程师,有时也不懂电脑。我可能会将这些信息记录到控制台,但是,正如我所说的,除了调试构建或内部工具之外,我很不情愿。不过,我想这确实取决于你的客户基础。

在极少数情况下,将 __LINE__给出的行更改为其他行会很有用。我见过 GNU configure 在一些测试中这样做,以便在它在原始源文件中没有出现的行之间插入一些巫术之后报告适当的行号。例如:

#line 100

将使以下行从 __LINE__100开始。您可以选择添加一个新的文件名

#line 100 "file.c"

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

#line BOOST_PP_ADD(__LINE__, 50)

我认为提到它是有用的,因为你问了关于 __LINE____FILE__的用法。从 C + + 中得到的惊喜总是不够:)

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

对于希望将用户 C 代码中报告的错误与用户源文件保持一致的预处理器来说,纠正 # line 非常有用。Yacc、 Lex 和(对我来说更熟悉的) ESQL/C 预处理器可以做到这一点。

仅供参考: g + + 提供了非标准的 _ _ PRETTY _ FUNCION _ _ 宏。直到刚才我才知道 C99 _ _ func _ _ (谢谢 Evan!).我想我还是更喜欢 _ _ PRETTY _ FUNCION _ _ 当它可用于额外的类范围时。

附注:

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. */
}

我一直在用。我唯一担心的是在日志文件中泄露 IP 地址。如果您的函数名真的很好,那么您可能会让一个商业秘密更容易被发现。这有点像发送调试符号,只是更难找到东西。99.999% 的病例不会有什么不好的结果。

C + + 20 std::source_location

C + + 终于增加了一个非宏选项,而且在未来某个时候,当 C + + 20变得普及时,它很可能会占据主导地位:

文件上说:

Function _ name () const noother;

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

其中 NTBS 的意思是“空终止字节串”。

这个特性在带有 -std=c++20的 GCC 11.2 Ubuntu 21.10上有,而在带有 g++-9 -std=c++2a的 GCC 9.1.0上没有。

Https://en.cppreference.com/w/cpp/utility/source_location 显示用法如下:

Main.cpp

#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!");
}

编译并运行:

g++ -std=c++20 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

产出:

info:main.cpp:17:int main() Hello world!

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

答案: _ _ PRETTY _ FUNCION _ _,_ _ FUNCION _ _,_ _ func _ _ 之间的区别是什么?