В чем разница между __PRETTY_FUNCTION__, __FUNCTION__, __func__?


Ответы:


266

__func__является неявно объявленным идентификатором, который расширяется до переменной массива символов, содержащей имя функции, когда он используется внутри функции. Он был добавлен в C в C99. От C99 §6.4.2.2 / 1:

Идентификатор __func__неявно объявляется транслятором, как если бы, сразу после открывающей скобки каждого определения функции, объявление

static const char __func__[] = "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 (в вашем источнике есть плавающая ссылка), что выглядит как победный ответ?
Matt Joiner

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
Тот же вывод из clang 3.5
Дончо Гунчев

Хорошо, но работает ли __func__он, когда он встроен в другую функцию? Допустим, у меня есть function1, она не принимает аргументов. function1 вызывает function2, которая включает __func__, какое имя функции будет напечатано, 1 или 2?
MarcusJ

@MarcusJ, почему бы не попробовать самому ... __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 Returns: Если этот объект представляет позицию в теле функции, возвращает 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 обновление 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 (void)

из A :: f ():
А <ИНТ, поплавок> :: е
е
void __cdecl A <int, float> :: f <bool> (void)

Использование __PRETTY_FUNCTION__триггеров необъявленной ошибки идентификатора, как и ожидалось.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.