__PRETTY_FUNCTION__, __FUNCTION__, __func__之间的区别是什么?它们被记录在哪里?我如何决定使用哪一个?


当前回答

__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__            " << __func__ << std::endl
                          << "__FUNCTION__        " << __FUNCTION__ << std::endl
                          << "__PRETTY_FUNCTION__ " << __PRETTY_FUNCTION__ << std::endl;
            }
            template <class T>
            static void f(double f) {
                (void)f;
                std::cout << "__PRETTY_FUNCTION__ " << __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

输出:

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

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

在Ubuntu 19.04, GCC 8.3.0中测试。

c++ 20 std:: source_location:: function_name

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 f(int i) {
    log("Hello world!"); // Line 16
    return i + 1;
}

int f(double i) {
    log("Hello world!"); // Line 21
    return i + 1.0;
}

int main() {
    f(1);
    f(1.0);
}

编译并运行:

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

输出:

info:source_location.cpp:16:int f(int) Hello world!
info:source_location.cpp:21:int f(double) Hello world!

所以注意它是如何返回调用者信息的,因此非常适合在日志记录中使用,请参见:是否有一种方法可以在c++函数中获得函数名?

提案:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1208r5.pdf

文件说:

const char* function_name() 6返回:如果该对象表示函数体中的位置, 类型对应的实现定义的NTBS 函数名。否则,返回空字符串。

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

在GCC 11.3 Ubuntu 22.04上测试。在g++-9 -std=c++2a的GCC 9.1.0上没有。

其他回答

对于那些想知道它在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>();
}

输出:

from main():
main
main
int __cdecl main(void)

from A::f():
A<int,float>::f
f
void __cdecl A<int,float>::f&ltbool>(void)

如预期的那样,使用__PRETTY_FUNCTION__会触发未声明的标识符错误。

__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. ftc .def.general]/8),这并不像C中的规范那么有用(最初向c++添加__func__的建议是N1642)。

__FUNCTION__是一些C编译器支持的标准前扩展(包括gcc和Visual c++);一般来说,你应该在支持__func__的地方使用它,只有在你使用的编译器不支持__FUNCTION__时才使用它(例如,Visual c++,它不支持C99,也不支持所有c++ 0x,不提供__func__)。

__PRETTY_FUNCTION__是一个gcc扩展,与__FUNCTION__基本相同,除了对于c++函数,它包含函数的“漂亮”名称,包括函数的签名。Visual c++有一个类似(但不完全相同)的扩展__FUNCSIG__。

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

__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__与C中的__func__相同,但在c++中它也包含类型签名。

__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__            " << __func__ << std::endl
                          << "__FUNCTION__        " << __FUNCTION__ << std::endl
                          << "__PRETTY_FUNCTION__ " << __PRETTY_FUNCTION__ << std::endl;
            }
            template <class T>
            static void f(double f) {
                (void)f;
                std::cout << "__PRETTY_FUNCTION__ " << __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

输出:

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

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

在Ubuntu 19.04, GCC 8.3.0中测试。

c++ 20 std:: source_location:: function_name

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 f(int i) {
    log("Hello world!"); // Line 16
    return i + 1;
}

int f(double i) {
    log("Hello world!"); // Line 21
    return i + 1.0;
}

int main() {
    f(1);
    f(1.0);
}

编译并运行:

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

输出:

info:source_location.cpp:16:int f(int) Hello world!
info:source_location.cpp:21:int f(double) Hello world!

所以注意它是如何返回调用者信息的,因此非常适合在日志记录中使用,请参见:是否有一种方法可以在c++函数中获得函数名?

提案:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1208r5.pdf

文件说:

const char* function_name() 6返回:如果该对象表示函数体中的位置, 类型对应的实现定义的NTBS 函数名。否则,返回空字符串。

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

在GCC 11.3 Ubuntu 22.04上测试。在g++-9 -std=c++2a的GCC 9.1.0上没有。

尽管没有完全回答最初的问题,但这可能是大多数人在谷歌上搜索这个的时候想看到的。

GCC:

$ cat test.cpp 
#include <iostream>

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