Яка різниця між __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__.

Щодо нестандартних макросів, вам потрібно ознайомитися з документацією вашого компілятора. Розширення Visual C ++ включені в документацію MSDN "Заздалегідь визначені макроси" компілятора C ++ . Розширення документації gcc описані на сторінці документації gcc "Імена функцій як рядки".


Чи можете ви зв’язатись із специфікацією C99 (у вашому джерелі є плаваюче посилання), як виглядає виграшна відповідь?
Метт Столяр

1
@ legends2k: Ні, це "рядок, визначений реалізацією" в C ++ 11. Це власне мова із специфікації. Див. § 8.4.1 [dcl.fct.def.general] / 8.
Джеймс Мак-Нілліс

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

Незважаючи на те, що не повністю відповів на початкове запитання, це, мабуть, те, що хотіла бачити більшість людей в гугле.

Для 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
Я знаю, що це не правильна відповідь, але це, мабуть, те, що хотіли бачити майже всі, хто це гугл :) (якщо вони ліниві спробувати себе)
Петро

5
Справедливий дзвінок, це приємно бачити.
Метт Столяр

13
Той самий вихід з кланг 3.5
Дончо Гунчев

Гаразд, але чи __func__працює, коли він вбудований в іншу функцію? Скажімо, у мене функція1, вона не бере аргументів. function1 викликає функцію2, яка включає __func__, яке ім'я функції буде надруковано, 1 або 2?
MarcusJ

@MarcusJ чому б не спробувати сам ... the __func__- це макрос, він переведе на будь-яку функцію, в якій ти зараз перебуваєш. Якщо ти введеш його у f1 і викличеш f1 у f2, ти завжди отримаєш f1.
Петро

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 ++

Випробувано в 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 ++", він все ще знаходиться у формі проекту.
Джеймс Мак-Нілліс

2
@JamesMcNellis Тепер, так що ясні коментарі, щоб зняти шум
daramarak

7

Для тих, хто цікавиться, як це проходить у VS.

MSVC 2015 Update 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>();
}

вихід:

з головного ():
головний
головний
int __cdecl main (недійсний)

від A :: f ():
A <int, float> :: f
f
void __cdecl A <int, float> :: f <bool> (void)

Використання __PRETTY_FUNCTION__тригерів недекларованої помилки ідентифікатора, як очікувалося.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.