__PRETTY_FUNCTION __,__ FUNCTION__和__func__有什么区别?


Answers:


266

__func__是一个隐式声明的标识符,当在函数内部使用该标识符时,它将扩展为包含函数名称的字符数组变量。它已添加到C99中的C中。从C99 §6.4.2.2/ 1:

标识符__func__由翻译器隐式声明,就像在紧随每个函数定义的大括号之后进行声明一样

static const char __func__[] = "function-name";

出现,其中function-name是词法包围函数的名称。此名称是函数的未经修饰的名称。

请注意,它不是宏,并且在预处理期间没有特殊含义。

__func__已被添加到C ++ 11中的C ++中,其中被指定为包含“实现定义的字符串”(C ++ 11§8.4.1[dcl.fct.def.general] / 8),可用作C中的规范。(最初添加__func__到C ++中的建议是N1642)。

__FUNCTION__是某些C编译器支持的标准扩展(包括gcc和Visual C ++);通常,应该__func__在受支持的地方使用它,并且仅__FUNCTION__在使用不支持它的编译器时才使用(例如,不支持C99并且尚不支持所有C ++ 0x的Visual C ++不支持)。提供__func__)。

__PRETTY_FUNCTION__是gcc扩展名,与__FUNCTION__g ++ 扩展名几乎相同,除了C ++函数外,它包含函数的“漂亮”名称,包括函数的签名。Visual C ++具有类似(但不完全相同)的扩展__FUNCSIG__

对于非标准宏,您将需要查阅编译器的文档。Visual C ++扩展包含在C ++编译器的“预定义宏”的MSDN文档中。gcc文档扩展在gcc文档页面“函数名称为字符串”中进行了描述


您可以链接到C99规范(您的源中有一个浮动链接),看起来像什么是成功的答案?
马特·乔纳

1
@ legends2k:不,它是C ++ 11中的“实现定义的字符串”。这是规范中的实际语言。参见§8.4.1[dcl.fct.def.general] / 8。
James McNellis 2013年

2
请注意,尽管gcc和VC都提供__FUNCTION__,但它们做的事情略有不同。gcc等于__func__。VC给出了未经修饰但仍经过修饰的名称。对于名为“ foo”的方法,gcc将为您提供"foo",VC将为您提供"my_namespace::my_class::foo"
阿德里安·麦卡锡

1
奇怪的是,我正在使用MSVC 2017 CE,当我键入__PRETTY_FUNCTION__它时,它确实显示在列表中,并且当我将鼠标移到它上面时,它确实显示了有关函数名称的信息,但是编译失败。
弗朗西斯·库格勒

1
@FrancisCugler我也对此感到惊讶!见我的问题就可以了stackoverflow.com/questions/48857887/...
亚当·巴杜拉

108

尽管没有完全回答原始问题,但是这可能是大多数使用Google搜索此功能的人想要看到的。

对于GCC:

petanb@debian:~$ cat test.cpp 
#include <iostream>

int main(int argc, char **argv)
{
    std::cout << __func__ << std::endl
              << __FUNCTION__ << std::endl
              << __PRETTY_FUNCTION__ << std::endl;
}
petanb@debian:~$ g++ test.cpp 
petanb@debian:~$ 
petanb@debian:~$ ./a.out 
main
main
int main(int, char**)

6
我知道这不是一个正确的答案,但这可能是几乎每个使用此方法的人都想看到的:)(如果他们懒惰地尝试一下)
Petr 2014年

5
公平电话,很高兴看到。
马特·乔纳

13
来自
c

好的,但是__func__当它嵌入到另一个函数中时可以工作吗?可以说我有function1,它没有参数。function1调用包含的function2,__func__将打印哪个函数名称,是1还是2?
MarcusJ

@MarcusJ为什么不自己尝试呢... ... __func__是一个宏,它将转换为您当前正在使用的任何函数。如果将其放入f1并在f2中调用f1,则始终会得到f1。
Petr

41

__PRETTY_FUNCTION__ 处理C ++功能:类,名称空间,模板和重载

main.cpp

#include <iostream>

namespace N {
    class C {
        public:
            template <class T>
            static void f(int i) {
                (void)i;
                std::cout << __func__ << std::endl
                          << __FUNCTION__ << std::endl
                          << __PRETTY_FUNCTION__ << std::endl;
            }
            template <class T>
            static void f(double f) {
                (void)f;
                std::cout << __PRETTY_FUNCTION__ << std::endl;
            }
    };
}

int main() {
    N::C::f<char>(1);
    N::C::f<void>(1.0);
}

编译并运行:

g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

输出:

f
f
static void N::C::f(int) [with T = char]
static void N::C::f(double) [with T = void]

您可能还对带有函数名称的堆栈跟踪感兴趣:C或C ++中的print调用堆栈

已在Ubuntu 19.04,GCC 8.3.0中测试。

C ++ 20 std::source_location::function_name

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1208r5.pdf进入了C ++ 20,因此我们还有另一种方法。

该文档说:

constexpr const char * function_name()const noexcept;

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

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

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

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!

因此请注意这是如何返回调用者信息的,因此非常适合在日志记录中使用,另请参见:是否可以在C ++函数中获取函数名?


13

__func__在C ++ 0x标准的8.4.1节中有说明。在这种情况下,它是以下形式的预定义函数局部变量:

static const char __func__[] = "function-name ";

其中“功能名称”是特定于实现的。这意味着无论何时声明一个函数,编译器都会将该变量隐式添加到您的函数中。同样是真正的__FUNCTION____PRETTY_FUNCTION__。尽管使用了大写字母,但它们不是宏。虽然__func__是C ++ 0x的补充

g++ -std=c++98 ....

仍将使用编译代码__func__

__PRETTY_FUNCTION____FUNCTION__http://gcc.gnu.org/onlinedocs/gcc-4.5.1/gcc/Function-Names.html#Function-Names中进行了记录。__FUNCTION__只是的另一个名字__func____PRETTY_FUNCTION____func__C语言相同,但在C ++语言中也包含类型签名。


__func__不是C ++ 03的一部分。它已在C ++ 0x中添加,但C ++ 0x尚未成为“ C ++标准”,仍处于草稿形式。
James McNellis

2
@JamesMcNellis现在,请清除注释以消除噪音
daramarak

7

对于那些想知道它在VS中如何运行的人。

MSVC 2015更新1,cl.exe版本19.00.24215.1:

#include <iostream>

template<typename X, typename Y>
struct A
{
  template<typename Z>
  static void f()
  {
    std::cout << "from A::f():" << std::endl
      << __FUNCTION__ << std::endl
      << __func__ << std::endl
      << __FUNCSIG__ << std::endl;
  }
};

void main()
{
  std::cout << "from main():" << std::endl
    << __FUNCTION__ << std::endl
    << __func__ << std::endl
    << __FUNCSIG__ << std::endl << std::endl;

  A<int, float>::f<bool>();
}

输出:

从main():
主要
主要
int __cdecl main(void)

来自A :: f():
A <int,float> :: f
F
无效__cdecl A <int,float> :: f <bool>(void)

__PRETTY_FUNCTION__按预期使用触发器未声明的标识符错误。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.