ความแตกต่างระหว่าง __PRETTY_FUNCTION__, __FUNCTION__, __func__ คืออะไร


221

อะไรคือความแตกต่างระหว่าง__PRETTY_FUNCTION__, __FUNCTION__, __func__และสถานที่ที่พวกเขาจะได้รับการบันทึก? ฉันจะตัดสินใจได้อย่างไรว่าควรใช้อันไหน

คำตอบ:


266

__func__เป็นตัวบ่งชี้ที่ประกาศโดยปริยายซึ่งขยายเป็นตัวแปรอาร์เรย์อักขระที่มีชื่อฟังก์ชันเมื่อใช้ภายในฟังก์ชัน มันถูกเพิ่มไปยัง C ใน C99 จากC99 §6.4.2.2 / 1:

ตัวบ่งชี้__func__ถูกประกาศโดยผู้แปลโดยนัยราวกับว่าทันทีหลังจากวงเล็บเปิดของคำจำกัดความของฟังก์ชันแต่ละรายการการประกาศ

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

ปรากฏขึ้นโดยที่ function-name คือชื่อของฟังก์ชั่นการปิดล้อมด้วยคำศัพท์ ชื่อนี้เป็นชื่อที่ไม่มีการตกแต่งของฟังก์ชัน

โปรดทราบว่ามันไม่ใช่แมโครและไม่มีความหมายพิเศษในระหว่างการประมวลผลล่วงหน้า

__func__ถูกเพิ่มใน C ++ ใน C ++ 11 โดยที่มันถูกระบุว่ามี "สตริงการนำไปปฏิบัติ" (C ++ 11 §8.4.1 [dcl.fct.def.general] / 8) ซึ่งไม่ค่อยเป็น มีประโยชน์ตามข้อกำหนดใน C (ข้อเสนอดั้งเดิมที่จะเพิ่ม__func__ใน C ++ คือN1642 )

__FUNCTION__เป็นส่วนขยายมาตรฐานล่วงหน้าที่คอมไพเลอร์ C บางตัวสนับสนุน (รวมถึง gcc และ Visual C ++) โดยทั่วไปคุณควรใช้__func__ตำแหน่งที่รองรับและใช้เฉพาะ__FUNCTION__เมื่อคุณใช้คอมไพเลอร์ที่ไม่รองรับ (ตัวอย่างเช่น Visual C ++ ซึ่งไม่รองรับ C99 และยังไม่รองรับ C ++ 0x ทั้งหมด แต่ไม่รองรับ ให้__func__)

__PRETTY_FUNCTION__เป็นส่วนขยาย gcc ที่ส่วนใหญ่เหมือนกับ__FUNCTION__ยกเว้นสำหรับฟังก์ชั่น C ++ มันมีชื่อ "สวย" ของฟังก์ชั่นรวมถึงลายเซ็นของฟังก์ชั่น Visual C ++ มีคล้ายกัน ( แต่ไม่เหมือนกันมาก) __FUNCSIG__ส่วนขยาย

สำหรับมาโครที่ไม่ได้มาตรฐานคุณจะต้องศึกษาเอกสารประกอบของคอมไพเลอร์ของคุณ c ++ Visual ส่วนขยายจะรวมอยู่ในเอกสาร MSDN ของ C ++ คอมไพเลอร์ของ"ที่กำหนดไว้ล่วงหน้าแมโคร" นามสกุลเอกสาร gcc อธิบายไว้ในหน้าเอกสารคู่มือ gcc "ชื่อฟังก์ชั่นเป็นสตริง"


คุณสามารถลิงค์ไปที่สเปค C99 (มีลิงค์ลอยในแหล่งที่มาของคุณ) สำหรับสิ่งที่ดูเหมือนว่าคำตอบที่ชนะ?
Matt Joiner

1
@ legends2k: ไม่เป็น "สตริงที่กำหนดการนำไปใช้งาน" ใน C ++ 11 นั่นคือภาษาจริงจากข้อกำหนด ดู§8.4.1 [dcl.fct.def.general] / 8
James McNellis

2
โปรดทราบว่าในขณะที่ทั้ง gcc และ VC มีให้__FUNCTION__แต่พวกเขาทำสิ่งที่แตกต่างกันเล็กน้อย GCC __func__ให้เทียบเท่า VC ให้ชื่อรุ่นที่ยังไม่ได้ตกแต่ง แต่ยังคงประดับอยู่ สำหรับวิธีการที่ชื่อ "foo" GCC จะทำให้คุณ"foo", VC "my_namespace::my_class::foo"จะให้
Adrian McCarthy

1
สิ่งที่อยากรู้คือฉันใช้ MSVC 2017 CE และเมื่อฉันพิมพ์__PRETTY_FUNCTION__มันจะปรากฏในรายการว่าพร้อมใช้งานและเมื่อฉันเลื่อนเมาส์ไปที่มันจะแสดงข้อมูลเกี่ยวกับชื่อฟังก์ชั่นอย่างไรก็ตามมันไม่สามารถรวบรวมได้
Francis Cugler

1
@ FrancisCugler ฉันรู้สึกประหลาดใจกับสิ่งนี้เช่นกัน! ดูคำถามของฉันเกี่ยวกับมันstackoverflow.com/questions/48857887/ …
Adam Badura

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
ฉันรู้ว่านี่ไม่ใช่คำตอบที่ถูกต้อง แต่อาจเป็นสิ่งที่เกือบทุกคนที่ google ต้องการจะเห็น :) (ถ้าพวกเขาขี้เกียจลองตัวเอง)
Petr

5
ยุติธรรมโทรนี้ดูดี
Matt Joiner

13
เอาท์พุทเดียวกันจาก clang 3.5
Doncho Gunchev

ตกลง แต่__func__ใช้ได้เมื่อฝังอยู่ในฟังก์ชันอื่นหรือไม่ ให้บอกว่าฉันมี function1 มันไม่มีข้อโต้แย้ง function1 เรียกใช้ function2 ที่รวม__func__ชื่อฟังก์ชันใดที่จะพิมพ์ 1 หรือ 2
MarcusJ

@MarcusJ ทำไมไม่ลองด้วยตัวเอง ... __func__มันเป็นมาโครมันจะแปลไปยังฟังก์ชั่นที่คุณใช้อยู่ถ้าคุณใส่มันลงใน f1 และเรียก f1 ใน f2 คุณจะได้ 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]

คุณอาจสนใจในการติดตามสแต็กที่มีชื่อฟังก์ชัน: call call stack ใน C หรือ C ++

ทดสอบใน 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 ถ่าน * function_name () const noexcept;

6 คืน: ถ้าวัตถุนี้แสดงถึงตำแหน่งในเนื้อความของฟังก์ชันส่งคืน NTBS ที่กำหนดใช้งานที่ควรสอดคล้องกับชื่อฟังก์ชัน มิฉะนั้นส่งคืนสตริงว่าง

โดยที่ NTBS หมายถึง "Null Terminated Byte String"

ฉันจะลองดูเมื่อการสนับสนุนมาถึง 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__ เหมือนกับใน C แต่ใน C ++ จะมีลายเซ็นประเภทเช่นกัน__func____PRETTY_FUNCTION____func__


__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 (เป็นโมฆะ)

จาก A :: f ():
<int, float> :: ฉ
ฉ
โมฆะ __cdecl A <int, float> :: f <bool> (void)

การใช้__PRETTY_FUNCTION__ทริกเกอร์ระบุข้อผิดพลาดที่ไม่ได้ประกาศตามที่คาดไว้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.