2009-07-10 17 views
5

Si consideri il seguente codice:C++ funzione di chiamata identificativo

void Foo() { 
    ...... 
    LOG_ERROR("I'm error 1") // call 1 
    ..... 
    LOG_ERROR("I'm error 2") // call 2 
    ..... 

} 

LOG_ERROR() è una macro. LOG_ERROR() dovrebbe stampare la stringa identificandolo nel codice, mentre il presupposto è che il codice può cambiare, ma A::Foo() rimarrà invariato. L'identificativo deve essere conservato mentre il codice cambia.

questo può essere risolto con l'aggiunta di codice di errore come argomento di LOG_ERROR(), ma vogliamo rimuovere dal programmatore l'onere di gestire l'errore codici.

L'utilizzo di __LINE__ non è una risposta, poiché Foo() può passare dalla build alla build .

Perciò ho pensato di identificare LOG_ERROR() relativa dell'inizio della Foo():

  • a. Identificare per nome file (__FILE__) + nome funzione (__FUNCTION__) + numero di riga LOG_ERROR() relativo a Foo() start.
  • b. Identificare per nome file (__FILE__) + nome funzione (__FUNCTION__) + LOG_ERROR() numero di chiamata in Foo().

La soluzione dovrebbe funzionare con VC++ 2008 e almeno g ++ 4.1.1.

Una soluzione proposta (link text) è:

#define ENABLE_LOG_ERROR static const int LOG_ERROR_start_line = __LINE__ 
#define LOG_ERROR(s) cerr << "error #" << (__LINE__ - LOG_ERROR_start_line) \ 
    << " in " << __func__ << ": " << s << endl 

void Foo() { 
    ENABLE_LOG_ERROR; 
    //... 
    LOG_ERROR("error 1"); 
    int i; 
    LOG_ERROR("error 2"); 
} 

Questo impone all'utente di scrivere ENABLE_LOG_ERROR in inizio di ogni funzione contenente LOG_ERROR() e ci sei molti tali funzioni.

Esiste un altro modo per eseguire l'operazione?

+0

@idimba, se non è troppo disturbo, è possibile utilizzare uno script per aggiungere automaticamente "ENABLE_LOG_ERROR;" subito dopo ogni nome di funzione. Io uso quel trucco quando esploro * codice di terze parti. –

+0

Bella idea, ma influenzerà __LINE__ che usiamo ampiamente nel nostro sottosistema di registrazione. – dimba

risposta

0

Modificando l'idea stack, utilizzare una mappatura std::map da std::string al conteggio e cercare il nome della funzione.

std::map<std::string, int> LOG_ERROR_count_map; 
#define LOG_ERROR(s) {\ 
    int count = ++LOG_ERROR_count_map[ __func__ ];\ 
    std::cout << count << " in " __func__ ": " s << std::endl;\ 
} 

Ciò significa che non è necessario il ENABLE_LOG_ERROR, ma a costo di una mappa look-up per ogni registro. (E 'un compromesso tra la facilità d'uso e del tempo.)

+0

Sia la mia soluzione che GMan non danno l'ennesima istanza di LOG_ERROR nella funzione, ma l'ennesima chiamata di LOG_ERROR nella funzione. Pertanto non possono essere utilizzati per semplicemente contare gli usi (che è probabilmente l'uso previsto) quando si verifica una ramificazione. Qualsiasi forma di metodo di conteggio dinamico non funzionerà. –

+0

È probabilmente ovvio, ma vale la pena sottolineare che non è possibile utilizzare il preprocessore per i conteggi statici in base al nome della funzione perché non c'è modo di rilevare quando la funzione è cambiata - le stringhe non possono essere confrontate. –

1

Questa soluzione è non standard, ma entrambi MSVC e supporto GCC __COUNTER__, che viene incrementato ogni volta che viene invocato.

#define LOG_ERROR(s) cerr << "error #" << (__COUNTER__) << " in " \ 
<< __func__ << ": " << s << endl 

noti che __COUNTER__ sarà ripristinato in ogni unità di compilazione, e solo in ogni unità di compilazione. Quindi se Foo() ha 7 macro LOG_ERROR(), in una funzione successiva Bar() il valore di __COUNTER__ sarà 7 per il primo utilizzo di LOG_ERROR().

+0

Tuttavia, ciò non funziona tra le funzioni. – GManNickG

+0

Il problema reale è che se si inserisce un nuovo messaggio, quelli sottostanti vengono tutti rinumerati. –

+0

@GMan: hai ragione che non si resetterà per ogni funzione. Ho modificato per chiarirlo. @Earwicker: anche corretto. Questa è una soluzione abbastanza fragile. Presuppone che i 'LOG_ERROR()' sono sempre e solo aggiunti dopo tutte le invocazioni precedenti nella stessa unità di compilazione. – Geerad

1

Mentre la domanda riguarda modi per generare identificatori di linea univoci all'interno di una funzione per scopi di registrazione, ho intenzione di fare un passo indietro e guardare il problema reale da risolvere: Come generare un output di registro che può facilmente identificare la linea del codice sorgente senza gravare sullo scrittore del codice.

Supponiamo che stiate incorporando una versione di build unica in ogni versione del vostro programma (che è una buona idea in generale). Supponiamo anche che tu stia usando un meccanismo di controllo del codice sorgente che mantiene la cronologia del tuo codice sorgente (che è anche una buona idea da fare comunque) e che può presentarti la fonte come era per qualsiasi versione di build richiesta del programma.

Se tali ipotesi sono vere, una soluzione è quella di far scrivere al programma la sua versione corrente nel file di registro. Quindi ogni singola voce di registrazione può semplicemente registrare il numero di linea tramite __LINE__.

Così, quando qualcuno ha bisogno di usare il registro: può guardare il numero di versione nel registro, prendere la fonte corrispondente dal repository di controllo del codice sorgente e usare i numeri di riga dal registro per andare alla fonte corretta Linee. Questo mette un po 'più di peso sulla persona che usa l'output del log. Tuttavia, se il codice registrato dipende o è influenzato da un altro codice che potrebbe cambiare da versione a versione, potrebbe comunque essere richiesto lo stato cronologico del codice sorgente.

Inoltre, un vantaggio di lavorare in questo modo è che rimuove la necessità di assumere che qualsiasi funzione data rimarrà invariata, come era originariamente parte della domanda. Quindi questo metodo ha un'applicazione molto più ampia.


quanto riguarda l'attuazione va, si potrebbe o accedere la versione del programma quando il programma si avvia o si potrebbe fare la macro di registrazione includerlo in ogni voce.

Se la versione del programma viene normalmente archiviata in un punto non facilmente accessibile nel normale codice sorgente, è possibile creare una fase di pre-generazione che estrae la versione e la scriva in un file version.h semplice come #define o const stringa. Quindi il codice di registrazione o la macro potrebbero utilizzarlo automaticamente per generare sempre la versione corrente del programma.

Problemi correlati