Sự khác biệt giữa __PRETTY_FUNCTION__, __FUNCTION__, __func__ là gì?


221

Sự khác biệt giữa những gì __PRETTY_FUNCTION__, __FUNCTION__, __func__, và nơi được họ ghi nhận? Làm thế nào để tôi quyết định sử dụng cái nào?

Câu trả lời:


266

__func__là một định danh được khai báo ngầm mở rộng thành một biến mảng ký tự chứa tên hàm khi nó được sử dụng bên trong hàm. Nó đã được thêm vào C trong C99. Từ C99 §6.4.2.2 / 1:

Mã định danh __func__được người dịch khai báo ngầm như thể, ngay sau dấu ngoặc mở của mỗi định nghĩa hàm, khai báo

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

đã xuất hiện, trong đó tên hàm là tên của hàm bao quanh từ vựng. Tên này là tên chưa được bổ sung của hàm.

Lưu ý rằng nó không phải là macro và nó không có ý nghĩa đặc biệt trong quá trình tiền xử lý.

__func__đã được thêm vào C ++ trong C ++ 11, trong đó nó được chỉ định là có chứa "chuỗi định nghĩa triển khai" (C ++ 11 §8.4.1 [dcl.fct.def.general] / 8), không hoàn toàn như hữu ích như đặc tả trong C. (Đề xuất ban đầu để thêm __func__vào C ++ là N1642 ).

__FUNCTION__là một phần mở rộng tiền chuẩn mà một số trình biên dịch C hỗ trợ (bao gồm gcc và Visual C ++); nói chung, bạn nên sử dụng __func__nơi nó được hỗ trợ và chỉ sử dụng __FUNCTION__nếu bạn đang sử dụng trình biên dịch không hỗ trợ nó (ví dụ: Visual C ++, không hỗ trợ C99 và chưa hỗ trợ tất cả C ++ 0x, không hỗ trợ cung cấp __func__).

__PRETTY_FUNCTION__là một phần mở rộng gcc gần giống như __FUNCTION__, ngoại trừ các hàm C ++, nó chứa tên "khá" của hàm bao gồm cả chữ ký của hàm. Visual C ++ có phần mở rộng tương tự (nhưng không hoàn toàn giống nhau) __FUNCSIG__.

Đối với các macro không chuẩn, bạn sẽ muốn tham khảo tài liệu của nhà biên dịch. Các phần mở rộng Visual C ++ được bao gồm trong tài liệu MSDN của " Trình biên dịch được xác định trước" của trình biên dịch C ++ . Các phần mở rộng tài liệu gcc được mô tả trong trang tài liệu gcc "Tên hàm dưới dạng chuỗi".


Bạn có thể liên kết đến đặc tả C99 (có một liên kết nổi trong nguồn của bạn), cho câu trả lời giống như chiến thắng không?
Matt Joiner

1
@ legends2k: Không, đó là "chuỗi xác định thực hiện" trong C ++ 11. Đó là ngôn ngữ thực tế từ đặc điểm kỹ thuật. Xem §8.4.1 [dcl.fct.def.general] / 8.
James McNellis

2
Lưu ý rằng trong khi cả gcc và VC cung cấp __FUNCTION__, chúng làm những việc hơi khác nhau. gcc cho tương đương với __func__. VC đưa ra phiên bản của tên chưa được trang trí nhưng vẫn được tô điểm. Đối với một phương thức có tên là "foo", gcc sẽ cung cấp cho bạn "foo", VC sẽ cung cấp cho bạn "my_namespace::my_class::foo".
Adrian McCarthy

1
Điều gây tò mò là tôi đang sử dụng MSVC 2017 CE và khi tôi gõ __PRETTY_FUNCTION__nó sẽ hiển thị trong danh sách là có sẵn và khi tôi di chuyển chuột qua nó, nó sẽ hiển thị thông tin về tên hàm, tuy nhiên nó không biên dịch được.
Francis Cugler

1
@FrancisCugler Tôi cũng ngạc nhiên về điều này! Xem câu hỏi của tôi trên đó stackoverflow.com/questions/48857887/ từ
Adam Badura

108

Mặc dù không trả lời đầy đủ câu hỏi ban đầu, nhưng đây có lẽ là điều mà hầu hết mọi người đang googling này muốn xem.

Đối với 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
Tôi biết đây không phải là một câu trả lời thích hợp, nhưng có lẽ đó là điều mà hầu hết mọi người google muốn xem :) (nếu họ lười thử bản thân)
Petr

5
Cuộc gọi công bằng, đây là tốt đẹp để xem.
Matt Joiner

13
Cùng một đầu ra từ clang 3.5
Doncho Gunchev

Ok, nhưng nó __func__hoạt động khi nó được nhúng trong một chức năng khác? Hãy nói rằng tôi có hàm1, nó không có đối số. function1 gọi function2 bao gồm __func__, tên hàm nào sẽ được in, 1 hoặc 2?
MarcusJ

@MarcusJ tại sao không tự mình thử ... đó __func__là một macro, nó sẽ dịch sang bất kỳ chức năng nào bạn đang làm. Nếu bạn đặt nó vào F1 và gọi F1 trong f2, bạn sẽ luôn nhận được F1.
Petr

41

__PRETTY_FUNCTION__ xử lý các tính năng C ++: lớp, không gian tên, mẫu và quá tải

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);
}

Biên dịch và chạy:

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

Đầu ra:

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

Bạn cũng có thể quan tâm đến dấu vết ngăn xếp với tên hàm: in ngăn xếp cuộc gọi trong C hoặc C ++

Đã thử nghiệm trong Ubuntu 19.04, GCC 8.3.0.

C ++ 20 std::source_location::function_name

http://www.open-std.org/jtc1/sc22/wg21/docs/ con / 2019 / p1208r5.pdf đã đi vào C ++ 20, vì vậy chúng tôi có một cách khác để làm điều đó.

Các tài liệu nói:

constexpr const char * function_name () const noexcept;

6 Trả về: Nếu đối tượng này đại diện cho một vị trí trong phần thân của hàm, trả về một NTBS được xác định thực hiện tương ứng với tên hàm. Nếu không, trả về một chuỗi rỗng.

Trong đó NTBS có nghĩa là "Chuỗi Byte Null chấm dứt".

Tôi sẽ dùng thử khi hỗ trợ đến GCC, GCC 9.1.0 mà g++-9 -std=c++2avẫn không hỗ trợ.

https://en.cppreference.com/w/cpp/utility/source_location yêu cầu sử dụng sẽ như sau:

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

Sản lượng có thể:

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

vì vậy hãy lưu ý cách điều này trả về thông tin người gọi và do đó hoàn hảo cho việc sử dụng trong ghi nhật ký, xem thêm: Có cách nào để lấy tên hàm bên trong hàm C ++ không?


13

__func__được ghi lại trong tiêu chuẩn C ++ 0x tại mục 8.4.1. Trong trường hợp này, đó là một biến cục bộ của hàm được xác định trước của biểu mẫu:

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

trong đó "tên hàm" là specfic thực hiện. Điều này có nghĩa là bất cứ khi nào bạn khai báo một hàm, trình biên dịch sẽ thêm biến này vào hàm của bạn. Điều tương tự cũng đúng __FUNCTION____PRETTY_FUNCTION__. Mặc dù có họ, họ không phải là macro. Mặc dù __func__là một bổ sung cho C ++ 0x

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

vẫn sẽ biên dịch mã bằng cách sử dụng __func__.

__PRETTY_FUNCTION____FUNCTION__được ghi lại ở đây http://gcc.gnu.org/onlinesocs/gcc-4.5.1/gcc/Function-Names.html#Function-Names . __FUNCTION__chỉ là một tên khác cho __func__. __PRETTY_FUNCTION__giống như __func__trong C nhưng trong C ++, nó cũng chứa chữ ký loại.


__func__không phải là một phần của C ++ 03. Nó đã được thêm vào trong C ++ 0x, nhưng C ++ 0x vẫn chưa phải là "tiêu chuẩn C ++", nó vẫn ở dạng bản nháp.
James McNellis

2
@JamesMcNellis Bây giờ, hãy xóa các bình luận, để loại bỏ tiếng ồn
daramarak

7

Đối với những người, những người tự hỏi làm thế nào nó đi trong VS.

MSVC 2015 Cập nhật 1, phiên bản 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>();
}

đầu ra:

từ chính ():
chủ yếu
chủ yếu
int __cdecl chính (void)

từ A :: f ():
Một <int, float> :: f
f
void __cdecl A <int, float> :: f <bool> (void)

Sử dụng các __PRETTY_FUNCTION__kích hoạt lỗi định danh không khai báo, như mong đợi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.