Jaka jest różnica między __PRETTY_FUNCTION__, __FUNCTION__, __func__?

221

Jaka jest różnica między __PRETTY_FUNCTION__, __FUNCTION__, __func__, i gdzie są one udokumentowane? Jak zdecydować, którego użyć?

Matt Joiner
źródło

Odpowiedzi:

266

__func__jest niejawnie zadeklarowanym identyfikatorem, który rozwija się do zmiennej tablicy znaków zawierającej nazwę funkcji, gdy jest używana wewnątrz funkcji. Został dodany do C w C99. Od C99 §6.4.2.2 / 1:

Identyfikator __func__jest domyślnie zadeklarowany przez tłumacza, tak jakby deklaracja znajdowała się bezpośrednio po nawiasie otwierającym każdej definicji funkcji

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

pojawił się, gdzie nazwa-funkcji jest nazwą funkcji zamykającej leksykalnie. Ta nazwa jest nie ozdobioną nazwą funkcji.

Należy pamiętać, że nie jest to makro i nie ma specjalnego znaczenia podczas przetwarzania wstępnego.

__func__został dodany do C ++ w C ++ 11, gdzie jest określony jako zawierający „ciąg zdefiniowany w implementacji” (C ++ 11 §8.4.1 [dcl.fct.def.general] / 8), co nie jest tak bardzo użyteczne jako specyfikacja w C. (Oryginalna propozycja dodania __func__do C ++ to N1642 ).

__FUNCTION__jest standardowym rozszerzeniem obsługiwanym przez niektóre kompilatory C (w tym gcc i Visual C ++); ogólnie powinieneś używać __func__tam, gdzie jest obsługiwany i używać tylko __FUNCTION__wtedy, gdy używasz kompilatora, który go nie obsługuje (na przykład Visual C ++, który nie obsługuje C99 i nie obsługuje jeszcze całego C ++ 0x, nie zapewnić __func__).

__PRETTY_FUNCTION__jest rozszerzeniem gcc, które jest w większości takie samo __FUNCTION__, z wyjątkiem tego, że dla funkcji C ++ zawiera „ładną” nazwę funkcji, w tym podpis funkcji. Visual C ++ ma podobne (ale nie do końca identyczne) rozszerzenie,__FUNCSIG__ .

W przypadku niestandardowych makr warto zajrzeć do dokumentacji kompilatora. Rozszerzenia Visual C ++ są zawarte w dokumentacji MSDN kompilatora C ++ „Predefiniowane makra” . Rozszerzenia dokumentacji gcc są opisane na stronie dokumentacji gcc „Nazwy funkcji jako ciągi znaków”.

James McNellis
źródło
Czy możesz połączyć się ze specyfikacją C99 (w źródle znajduje się swobodny link), na co wygląda zwycięska odpowiedź?
Matt Joiner,
1
@ legends2k: Nie, jest to „ciąg zdefiniowany w implementacji” w C ++ 11. To jest rzeczywisty język ze specyfikacji. Patrz § 8.4.1 [dcl.fct.def.general] / 8.
James McNellis,
2
Zauważ, że chociaż zarówno gcc, jak i VC zapewniają __FUNCTION__, robią nieco inne rzeczy. gcc daje równowartość __func__. VC podaje nieudekorowaną, ale wciąż ozdobioną wersję nazwy. W przypadku metody o nazwie „foo”, gcc da ci "foo", VC da "my_namespace::my_class::foo".
Adrian McCarthy
1
Co ciekawe, używam MSVC 2017 CE i kiedy piszę __PRETTY_FUNCTION__, pojawia się na liście jako dostępny, a kiedy na nim przesunę kursor myszy, wyświetla informacje o nazwie funkcji, jednak nie kompiluje się.
Francis Cugler,
1
@FrancisCugler Byłem tym również zaskoczony! Zobacz moje pytanie stackoverflow.com/questions/48857887/...
Adam Badura
108

Mimo, że nie udało się w pełni odpowiedzieć na pierwotne pytanie, prawdopodobnie właśnie to chciało zobaczyć większość osób odwiedzających google.

W przypadku 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**)
Petr
źródło
6
Wiem, że to nie jest właściwa odpowiedź, ale prawdopodobnie właśnie to chciał zobaczyć prawie każdy, kto szukał tego w Google :) (jeśli są leniwi, żeby spróbować)
Petr
5
Fair call, miło to widzieć.
Matt Joiner
13
Ten sam wynik z
klangu
Ok, ale __func__działa, gdy jest osadzony w innej funkcji? Powiedzmy, że mam funkcję1, nie wymaga żadnych argumentów. funkcja 1 wywołuje funkcję 2, która obejmuje __func__, która nazwa funkcji zostanie wydrukowana, 1 czy 2?
MarcusJ
@MarcusJ, dlaczego nie spróbować sam ... __func__to makro, to tłumaczy na dowolną funkcję, w której się obecnie znajdujesz. Jeśli umieścisz go w f1 i wywołasz f1 w f2, zawsze dostaniesz f1.
Petr
41

__PRETTY_FUNCTION__ obsługuje funkcje C ++: klasy, przestrzenie nazw, szablony i przeciążenie

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

Skompiluj i uruchom:

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

Wynik:

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

Mogą Cię również zainteresować ślady stosu z nazwami funkcji: wydrukuj stos wywołań w C lub C ++

Testowane w 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 przeszedł do C ++ 20, więc mamy jeszcze inny sposób, aby to zrobić.

Dokumentacja mówi:

constexpr const char * nazwa_funkcji () const noexcept;

6 Zwraca: Jeśli ten obiekt reprezentuje pozycję w treści funkcji, zwraca zdefiniowaną implementację NTBS, która powinna odpowiadać nazwie funkcji. W przeciwnym razie zwraca pusty ciąg.

gdzie NTBS oznacza „Ciąg bajtów zakończony zerem”.

Spróbuję, kiedy wsparcie dotrze do GCC, GCC 9.1.0 g++-9 -std=c++2anadal go nie obsługuje.

https://en.cppreference.com/w/cpp/utility/source_location roszczenia będą wyglądać następująco:

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

Możliwe wyjście:

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

więc zwróć uwagę, jak to zwraca informacje o dzwoniącym, a zatem idealnie nadaje się do użycia przy logowaniu, zobacz także: Czy istnieje sposób na uzyskanie nazwy funkcji wewnątrz funkcji C ++?

Ciro Santilli
źródło
13

__func__jest udokumentowane w standardzie C ++ 0x w sekcji 8.4.1. W tym przypadku jest to predefiniowana lokalna zmienna funkcji w formie:

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

gdzie „nazwa funkcji” jest specyficzna dla implementacji. Oznacza to, że za każdym razem, gdy deklarujesz funkcję, kompilator doda tę zmienną niejawnie do twojej funkcji. To samo dotyczy __FUNCTION__i __PRETTY_FUNCTION__. Mimo górnej części nie są makrami. Chociaż __func__jest dodatkiem do C ++ 0x

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

nadal będzie kompilować kod za pomocą __func__.

__PRETTY_FUNCTION__i __FUNCTION__są udokumentowane tutaj http://gcc.gnu.org/onlinedocs/gcc-4.5.1/gcc/Function-Names.html#Function-Names . __FUNCTION__to po prostu inna nazwa dla __func__. __PRETTY_FUNCTION__jest taki sam jak __func__w C, ale w C ++ zawiera również podpis typu.

sashang
źródło
__func__nie jest częścią C ++ 03. Został dodany w C ++ 0x, ale C ++ 0x nie jest jeszcze „standardem C ++”, ale nadal jest w wersji roboczej.
James McNellis,
2
@JamesMcNellis To jest teraz, więc wyczyść komentarze, aby usunąć hałas
daramarak
7

Dla tych, którzy zastanawiają się, jak to wygląda w VS.

MSVC 2015 Update 1, cl.exe wersja 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>();
}

wynik:

z main ():
Główny
Główny
int __cdecl main (void)

z A :: f ():
A <int, float> :: f
fa
void __cdecl A <int, float> :: f <bool> (void)

Korzystanie z __PRETTY_FUNCTION__wyzwalaczy niezadeklarowany błąd identyfikatora, zgodnie z oczekiwaniami.

Finnan
źródło