Używanie __FILE__, __LINE__ i __FUNCTION__ w C ++

158

Zakładając, że C ++ wspiera je kompilatora, czy jest jakiś szczególny powód nie do użytku __FILE__, __LINE__a __FUNCTION__do rejestrowania i celów debugowania?

Przede wszystkim interesuje mnie podawanie użytkownikowi wprowadzających w błąd danych - na przykład zgłaszanie nieprawidłowego numeru linii lub funkcji w wyniku optymalizacji - lub przyjmowanie w rezultacie spadku wydajności.

Zasadniczo mogę zaufać __FILE__, __LINE__i __FUNCTION__aby zawsze zrobić dobry uczynek?

Runcible
źródło
LINE powinna postąpić właściwie. Używałem go szeroko i jego kohort, w tym PRETTY_FUNCTION . ... Ale ... cóż, właśnie teraz patrzę na kod, w którym leży LINE . Prawdopodobnie dlatego, że znajduje się w bloku catch do obsługi wyjątków try / catch.
Krazy Glew,
4
istotne: referencje gcc dla predefiniowanych makr
Alexander Malakhov

Odpowiedzi:

191

__FUNCTION__jest niestandardowy, __func__istnieje w C99 / C ++ 11. Pozostałe ( __LINE__i __FILE__) są po prostu w porządku.

Zawsze zgłasza właściwy plik i wiersz (oraz funkcję, jeśli zdecydujesz się użyć __FUNCTION__/ __func__). Optymalizacja nie jest czynnikiem, ponieważ jest rozwijaniem makr w czasie kompilacji; będzie nie wpływa w żaden sposób wydajność.

Evan Teran
źródło
3
__func__jest rodzajem problemu w C ++. C99 nie mówi ani słowa o domyślnych argumentach i tak dalej, w przypadkach, w których nie jest tak oczywiste, jak __func__powinno się zachować w C ++.
wilhelmtell
4
@thr: chociaż masz rację. Było całkiem jasne, że __func__istnieje w C99, a nie w C ++. Niezależnie od tego, myślę, że rozsądna implementacja __func__w c ++ spowodowałaby po prostu zniekształconą nazwę. Ponieważ nie jestem autorem kompilatorów, nie jest to moja sprawa.
Evan Teran,
Które kompilatory __FUNCTION__w ogóle nie obsługują ? Które kompilatory oprócz niedawnego gcc traktują to jako zmienną, a nie makro?
dorzecze
36
__func__jest teraz w standardzie C ++ 11.
VX
38

W rzadkich przypadkach może być przydatna zmiana podanej linii __LINE__na inną. Widziałem, że GNU configure robi to w niektórych testach, aby zgłosić odpowiednie numery wierszy po wstawieniu jakiegoś voodoo między wierszami, które nie pojawiają się w oryginalnych plikach źródłowych. Na przykład:

#line 100

Następujące linie będą zaczynać się od __LINE__100. Opcjonalnie możesz dodać nową nazwę pliku

#line 100 "file.c"

Rzadko się przydaje. Ale jeśli jest to potrzebne, nie znam żadnych alternatyw. W rzeczywistości zamiast wiersza można również użyć makra, które musi skutkować jedną z dwóch powyższych form. Korzystając z biblioteki preprocesora boost, możesz zwiększyć bieżący wiersz o 50:

#line BOOST_PP_ADD(__LINE__, 50)

Pomyślałem, że warto o tym wspomnieć, ponieważ zapytałeś o użycie __LINE__i __FILE__. C ++ nigdy nie daje wystarczająco dużo niespodzianek :)

Edycja: @Jonathan Leffler podaje kilka innych dobrych przypadków użycia w komentarzach:

Komunikowanie się z #line jest bardzo przydatne dla preprocesorów, które chcą zachować błędy zgłaszane w kodzie C użytkownika zgodnie z plikiem źródłowym użytkownika. Yacc, Lex i (bardziej dla mnie w domu) preprocesory ESQL / C robią to.

Johannes Schaub - litb
źródło
29

FYI: g ++ oferuje niestandardowe makro __PRETTY_FUNCTION__. Do tej pory nie wiedziałem o C99 __func__ (dzięki Evan!). Myślę, że nadal wolę __PRETTY_FUNCTION__, gdy jest on dostępny dla dodatkowego zakresu klas.

PS:

static string  getScopedClassMethod( string thePrettyFunction )
{
  size_t index = thePrettyFunction . find( "(" );
  if ( index == string::npos )
    return thePrettyFunction;  /* Degenerate case */

  thePrettyFunction . erase( index );

  index = thePrettyFunction . rfind( " " );
  if ( index == string::npos )
    return thePrettyFunction;  /* Degenerate case */

  thePrettyFunction . erase( 0, index + 1 );

  return thePrettyFunction;   /* The scoped class name. */
}
Panie Ree
źródło
2
Miło wiedzieć o __PRETTY_FUNCTION__. Bardzo przydatne!
Zheng Qu
8

Osobiście niechętnie używam ich do niczego poza debugowaniem wiadomości. Zrobiłem to, ale staram się nie pokazywać tego rodzaju informacji klientom lub użytkownikom końcowym. Moi klienci nie są inżynierami i czasami nie znają się na komputerach. Mógłbym zarejestrować te informacje w konsoli, ale, jak powiedziałem, niechętnie, z wyjątkiem kompilacji debugowania lub narzędzi wewnętrznych. Przypuszczam jednak, że zależy to od bazy klientów, którą masz.

Craig S
źródło
29
„Mogę zapisać te informacje na konsoli” - lub jeszcze lepiej: zaloguj się do pliku, aby jeśli coś pójdzie nie tak, możesz poprosić klienta o przesłanie go do Ciebie ...
Christoph,
7

C ++ 20 std::source_location

C ++ w końcu dodał opcję niebędącą makrami i prawdopodobnie będzie dominować w pewnym momencie w przyszłości, gdy C ++ 20 stanie się powszechny:

Dokumentacja mówi:

constexpr const char * function_name () const noexcept;

6 Zwraca: Jeśli ten obiekt reprezentuje pozycję w treści funkcji, zwraca zdefiniowany w implementacji NTBS, który powinien odpowiadać nazwie funkcji. W przeciwnym razie zwraca pusty ciąg.

gdzie NTBS oznacza „ciąg bajtów zakończony wartością zerową”.

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

Oświadczenia https://en.cppreference.com/w/cpp/utility/source_location 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!

__PRETTY_FUNCTION__vs __FUNCTION__vs __func__vsstd::source_location::function_name

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

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
1
W <experimental/source_location>aktualnym gcc-9.
陈浩南
5

Używam ich cały czas. Jedyne, o co się martwię, to podawanie adresu IP w plikach dziennika. Jeśli nazwy Twoich funkcji są naprawdę dobre, możesz ułatwić odkrycie tajemnicy handlowej. To trochę jak wysyłanie z symbolami debugowania, tylko trudniej jest znaleźć rzeczy. W 99,999% przypadków nic złego z tego nie wyniknie.

JeffCharter
źródło
1
Warto poruszyć. Wyodrębnienie tych informacji za pomocą stringsnarzędzia do wyodrębnienia wszystkich danych podobnych do ciągu z pliku wykonywalnego jest trywialne . Można wyodrębnić nawet skompresowane pliki wykonywalne. Uważaj na to, co wysyłasz do witryny klienta. Często zdarza się, że konkurenci są w stanie zdobyć Twoje pliki wykonywalne, nawet jeśli nie powinni tego robić.
Marty