Utilizzo di __FILE__, __LINE__ e __FUNCTION__ in C ++


158

Supponendo che il compilatore C ++ li supporti, esiste qualche motivo particolare per non utilizzarlo__FILE__ , __LINE__e __FUNCTION__per la registrazione e il debugging?

Mi occupo principalmente di fornire all'utente dati fuorvianti, ad esempio riportare un numero di riga errato o una funzione come risultato dell'ottimizzazione, o subire un calo delle prestazioni di conseguenza.

In sostanza, posso fidarmi __FILE__, __LINE__e __FUNCTION__per sempre fare la cosa giusta?


LINE dovrebbe fare la cosa giusta. L'ho usato ampiamente e le sue coorti, tra cui PRETTY_FUNCTION . ... Ma ... beh, sto proprio ora guardando il codice in cui si trova LINE . Probabilmente perché si trova in un blocco catch per la gestione delle eccezioni try / catch.
Krazy Glew,

Risposte:


191

__FUNCTION__non è standard, __func__esiste in C99 / C ++ 11. Gli altri ( __LINE__e __FILE__) stanno bene.

Riporterà sempre il file e la riga giusti (e funzionerà se scegli di usare __FUNCTION__/ __func__). L'ottimizzazione non è un fattore poiché è un'espansione macro in fase di compilazione; sarà mai effettuare prestazioni in alcun modo.


3
__func__è una specie di problema in C ++. C99 non dice una parola sugli argomenti predefiniti e così via, casi in cui non è così ovvio come __func__dovrebbe comportarsi in C ++.
Wilhelmtell,

4
@thr: mentre fai un buon punto. Ero abbastanza chiaro che __func__esiste in c99, non in c ++. Indipendentemente da ciò, penso che una ragionevole implementazione di __func__in c ++ comporterebbe solo il nome alterato. Dal momento che non sono uno scrittore di compilatori, non è proprio la mia chiamata.
Evan Teran,

Quali compilatori non supportano __FUNCTION__affatto? Quali compilatori, tranne il recente gcc, lo trattano come una variabile, non una macro?
Bacino

36
__func__è ora in standard C ++ 11.
VX,

38

In rari casi, può essere utile cambiare la linea data da __LINE__qualcos'altro. Ho visto GNU configurarlo per alcuni test per riportare i numeri di riga appropriati dopo aver inserito del voodoo tra le righe che non compaiono nei file sorgente originali. Per esempio:

#line 100

Le seguenti righe inizieranno con __LINE__100. Se lo desideri, puoi aggiungere un nuovo nome file

#line 100 "file.c"

È solo raramente utile. Ma se è necessario, non ci sono alternative che conosco. In realtà, al posto della riga, è possibile utilizzare anche una macro che deve risultare in una delle due forme precedenti. Utilizzando la libreria di preprocessore boost, è possibile incrementare la riga corrente di 50:

#line BOOST_PP_ADD(__LINE__, 50)

Ho pensato che fosse utile menzionarlo da quando hai chiesto informazioni sull'uso di __LINE__e __FILE__. Non si ottengono mai abbastanza sorprese dal C ++ :)

Modifica: @Jonathan Leffler fornisce alcuni buoni casi d'uso nei commenti:

La confusione con #line è molto utile per i pre-processori che vogliono mantenere gli errori riportati nel codice C dell'utente in linea con il file sorgente dell'utente. Yacc, Lex e (più a casa per me) i preprocessori ESQL / C lo fanno.


29

FYI: g ++ offre la macro non standard __PRETTY_FUNCTION__. Fino ad ora non sapevo di C99 __func__ (grazie Evan!). Penso di preferire ancora __PRETTY_FUNCTION__ quando è disponibile per l'ambito della classe extra.

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. */
}

2
Bello sapere di __PRETTY_FUNCTION__. Molto utile!
Zheng Qu,

8

Personalmente, sono riluttante a usarli per tutto tranne che per il debug dei messaggi. L'ho fatto, ma cerco di non mostrare quel tipo di informazioni ai clienti o agli utenti finali. I miei clienti non sono ingegneri e talvolta non sono esperti di computer. Potrei registrare queste informazioni sulla console, ma, come ho detto, a malincuore, tranne per le build di debug o per gli strumenti interni. Suppongo che dipenda dalla base di clienti che hai, però.


29
"Potrei registrare queste informazioni sulla console" - o meglio ancora: registrale in un file in modo che se qualcosa va storto puoi chiedere al cliente di inviarlo a te ...
Christoph,

7

C ++ 20 std::source_location

Il C ++ ha finalmente aggiunto un'opzione non macro e probabilmente dominerà ad un certo punto in futuro quando il C ++ 20 diventerà diffuso:

La documentazione dice:

constexpr const char * nome_funzione () const noexcept;

6 Restituisce: se questo oggetto rappresenta una posizione nel corpo di una funzione, restituisce un NTBS definito dall'implementazione che dovrebbe corrispondere al nome della funzione. Altrimenti, restituisce una stringa vuota.

dove NTBS significa "Null Terminated Byte String".

Ci proverò quando arriva il supporto a GCC, GCC 9.1.0 con g++-9 -std=c++2a ancora non lo supporta.

https://en.cppreference.com/w/cpp/utility/source_location afferma che l'utilizzo sarà simile a:

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

Uscita possibile:

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

__PRETTY_FUNCTION__vs __FUNCTION__vs __func__vsstd::source_location::function_name

Risposta a: Qual è la differenza tra __PRETTY_FUNCTION__, __FUNCTION__, __func__?


1
C'è <experimental/source_location>nell'attuale gcc-9.
陈浩南

5

Li uso sempre. L'unica cosa di cui mi preoccupo è regalare IP nei file di registro. Se i nomi delle tue funzioni sono davvero validi, potresti rendere più semplice scoprire un segreto commerciale. È un po 'come spedire con simboli di debug, è solo più difficile trovare cose. Nel 99,999% dei casi non ne verrà fuori nulla di male.


1
Buon punto per sollevare. È banale estrarre queste informazioni usando l' stringsutilità per estrarre tutti i dati simili a stringhe dall'eseguibile. È possibile estrarre anche eseguibili compressi. Prestare molta attenzione a ciò che si invia a un sito cliente. Spesso i concorrenti sono in grado di mettere le mani sui tuoi eseguibili, anche se non dovrebbero farlo.
Marty,

È possibile con constexpr che utilizza una tabella di ricerca o XOR bit a bit, ecc. Per offuscare qualsiasi letterale stringa nel tuo binario. Ci sono vari esempi là fuori se cerchi. Ovviamente l'offuscamento è solo offuscamento e non sicurezza, ma se non vuoi rendere evidenti i tuoi file e le tue funzioni nel binario, è un'opzione.
idij
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.