2009-08-10 5 views
9

qual è la buona pratica per generare un output dettagliato? Attualmente, ho una funzioneChe cosa è una buona pratica per generare un output dettagliato?

bool verbose; 
int setVerbose(bool v) 
{ 
    errormsg = ""; 
    verbose = v; 
    if (verbose == v) 
     return 0; 
    else 
     return -1; 
} 

e ogni volta che voglio generare output, faccio qualcosa di simile

if (debug) 
    std::cout << "deleting interp" << std::endl; 

tuttavia, non credo che sia molto elegante. quindi mi chiedo quale sarebbe un buon modo per implementare questo switch di verbosità?

+1

dosen't vostro 'setVerbose' sempre ritornare 0? – Kredns

+0

sì, a meno che non accada qualcosa di estremamente esoterico. è solo che ho un sacco di funzioni setSomething() che restituiscono tutti 0 se l'operazione ha avuto successo e -1 se non lo è. quindi è solo questione di avere un'interfaccia coerente ... –

+0

Non capisco questo codice.È un caso di editing andato storto? –

risposta

9

Il modo più semplice è quello di creare piccola classe come segue (qui è la versione Unicode, ma si può facilmente cambiare alla versione a singolo byte):

#include <sstream> 
#include <boost/format.hpp> 
#include <iostream> 
using namespace std; 

enum log_level_t { 
    LOG_NOTHING, 
    LOG_CRITICAL, 
    LOG_ERROR, 
    LOG_WARNING, 
    LOG_INFO, 
    LOG_DEBUG 
}; 

namespace log_impl { 
class formatted_log_t { 
public: 
    formatted_log_t(log_level_t level, const wchar_t* msg) : fmt(msg), level(level) {} 
    ~formatted_log_t() { 
     // GLOBAL_LEVEL is a global variable and could be changed at runtime 
     // Any customization could be here 
     if (level <= GLOBAL_LEVEL) wcout << level << L" " << fmt << endl; 
    }   
    template <typename T> 
    formatted_log_t& operator %(T value) { 
     fmt % value; 
     return *this; 
    }  
protected: 
    log_level_t  level; 
    boost::wformat  fmt; 
}; 
}//namespace log_impl 
// Helper function. Class formatted_log_t will not be used directly. 
template <log_level_t level> 
log_impl::formatted_log_t log(const wchar_t* msg) { 
    return log_impl::formatted_log_t(level, msg); 
} 

funzione Helper log è stata fatta template per ottenere sintassi di chiamata bello. Quindi potrebbe essere utilizzato nel modo seguente:

int main() 
{ 
    // Log level is clearly separated from the log message 
    log<LOG_DEBUG>(L"TEST %3% %2% %1%") % 5 % 10 % L"privet"; 
    return 0; 
} 

si potrebbe cambiare livello di dettaglio in fase di esecuzione, cambiando variabile globale GLOBAL_LEVEL.

+0

mi piace questa risposta. Ho davvero bisogno solo di questo semplice stdout di registrazione, nessun file/rete di cose mai necessarie (spero). quindi cercherò di mantenere basso il numero di dipendenze esterne e di adottare la mia implementazione. –

+0

Trovo questa soluzione intelligente e molto utile, vorrei poter dare 10 voti positivi! :-) –

+0

Credo che la variabile GLOBAL_LEVEL sia problematica tra le unità di traduzione, vero? Come implementeresti questa variabile? – quimnuss

3

Si potrebbe utilizzare log4cpp

+0

Dispone di funzionalità di registrazione asincrona? – Alex

+0

Non so se lo fa o no. scusa. – Glen

+3

Concordo sul fatto che l'uso di una corretta libreria di registrazione sia la risposta piuttosto che la codifica manuale, anche se Log4cpp sembra essere moribondo. http://logging.apache.org/log4cxx/index.html d'altra parte è vivo e vegeto. Secondo il sito web ha il supporto per la registrazione asincrona, anche se non penso che sia rilevante per questa domanda. –

3

si può avvolgere la funzionalità in una classe che supporta il < < operatore, che ti permette di fare qualcosa di simile

class Trace { 
    public: 
     enum { Enable, Disable } state; 
    // ... 
    operator<<(...) 
}; 

Poi si può fare qualcosa di simile

trace << Trace::Enable; 
trace << "deleting interp" 
+0

Mi piace l'idea di avvolgere un sistema di registrazione degli errori in una classe. È possibile memorizzare anche cose come la posizione del file di output e quant'altro nella classe. – Brian

+0

solo così ho capito bene: vorrei creare prima un'istanza della classe Trace chiamata trace? e poi un'altra domanda: perché Trace :: Enable è sufficiente per impostare lo stato su Enable? Sono ancora abbastanza nuovo per C++, e sono un po 'confuso. forse alcune tre righe in più nella tua idea mi aiuterebbero a capire meglio cosa sta succedendo. ma mi piace decisamente la tua idea! –

+1

Sì, è necessario creare un'istanza della classe Trace; questo potrebbe essere statico o locale per ciascun componente che desiderava usarlo. Trace :: Enable abilita/disabilita un flag interno all'interno della classe Trace (un'istanza dello stato enum). Questo sarebbe molto simile a std :: hex consente a std :: cout di produrre in formato esadecimale anziché decimale. Poiché si scriverà l'operatore << è possibile supportare Trace :: Enable (o qualsiasi altro identificatore di stato come verbosità e così) – ezpz

8
enum LogLevel 
{ 
    INF = 0, 
    WAR = 1, 
    ERR = 2 
}; 

LogLevel threshold = WAR; 

class mystreambuf: public std::streambuf {}; 
mystreambuf nostreambuf; 
std::ostream nocout(&nostreambuf); 
#define log(x) ((x >= threshold)? std::cout : nocout) 

int main() 
{ 
    log(INF) << "No hello?" << std::endl;  // Not printed on console, too low log level. 
    log(ERR) << "Hello world!" << std::endl; // Will print. 
    return 0; 
} 
+0

ci sarebbe la possibilità di "includere" lo std :: endl nella definizione log()? –

+0

Definire 'log (x)' può essere facilmente sostituito con la funzione inline. C'è un motivo per usare definisce qui? –

+0

Per quanto riguarda std :: endl: nessuno che io conosca. Se vuoi meno codice, puoi sempre usare typedef, come _e. Regardig # define/inline: nessun motivo. –

2

1. Se si utilizza g ++ è possibile utilizzare il flag -D, ciò consente al compilatore di definire una macro di propria scelta.

Definizione del

Per esempio:

#ifdef DEBUG_FLAG 
printf("My error message"); 
#endif 

2. Sono d'accordo che non è elegante o, in modo per renderlo un po 'più bello:

void verbose(const char * fmt, ...) 
{ 
va_list args; /* Used as a pointer to the next variable argument. */ 
va_start(args, fmt); /* Initialize the pointer to arguments. */ 

#ifdef DEBUG_FLAG 
printf(fmt, &args); 
#endif 
/*This isn't tested, the point is to be able to pass args to 
printf*/ 
} 

Che potrebbe utilizzare come printf:

verbose("Error number %d\n",errorno); 

3. Una terza soluzione più semplice e più simile a C++ e Unix è passare un argomento al programma che verrà utilizzato, come la macro in precedenza, per inizializzare una variabile particolare (che potrebbe essere un const globale).

Esempio: $ ./myprogram -v

if(optarg('v')) static const verbose = 1;