2009-04-17 16 views
30

Sto cercando di creare una classe di registrazione che abbia membri come Info, Error etc che possano essere configurati in output su console, file o in nessun posto.Implementazione di un no-op std :: ostream

Per efficienza, vorrei evitare il sovraccarico della formattazione dei messaggi che verranno gettati via (ovvero i messaggi di informazioni quando non sono in esecuzione in una modalità dettagliata). Se implemento uno std :: streambuf personalizzato che viene esportato da nessuna parte, immagino che il layer std :: ostream continui a fare tutta la formattazione. Qualcuno può suggerire un modo per avere uno std :: ostream veramente "nullo" che eviti di fare alcun lavoro sui parametri passati ad esso con <<?

Grazie.

+0

Non mi preoccuperei. basta usare un flusso nullo come mostrato da Neil. la classe non ha bisogno di prestazioni migliori, perché ovviamente se non si ha un obiettivo nullo, la formattazione * deve * essere fatta, quindi ovviamente non è fondamentale. solo i miei 2 centesimi –

+0

hmm, ma sembra che sia inteso come un "debug output" cosa? un modo che ho visto è così: out() << a << b ...; e out() restituisce struct f {}; senza modello f const & operator << (f const & f_, T const) {return f_; }, e quindi rendere restituire diverse strutture a seconda del livello di registro. o rendere diverse funzioni o qualsiasi altra cosa. –

risposta

4

Per evitare che le invocazioni operator<<() eseguano la formattazione, è necessario conoscere lo streamtype in fase di compilazione. Questo può essere fatto con macro o con modelli.

La mia soluzione modello segue.

class NullStream { 
public: 
    void setFile() { /* no-op */ } 
    template<typename TPrintable> 
    NullStream& operator<<(TPrintable const&) 
    { /* no-op */ } 
} 

template<class TErrorStream> // add TInfoStream etc 
class Logger { 
public: 
    TErrorStream& errorStream() { 
     return m_errorStream; 
    } 

private: 
    TErrorStream m_errorStream; 
}; 

//usage 
int main() { 
    Logger<std::ofstream> normal_logger; // does real output 
    normal_logger.errorStream().open("out.txt"); 
    normal_logger.errorStream() << "My age is " << 19; 

    Logger<NullStream> null_logger; // does zero output with zero overhead 
    null_logger.errorStream().open("out.txt"); // no-op 
    null_logger.errorStream() << "My age is " << 19; // no-op 
} 

Dal momento che si deve fare questo al momento della compilazione, è naturalmente del tutto inflessibile.

Ad esempio, non è possibile decidere il livello di registrazione in fase di esecuzione da un file di configurazione.

+0

+1: semplice, pulito, fa bene il lavoro. Nota che ad es."NullStream s; s << costose_funzione();" molto probabilmente valuterà ancora costosi_funzione(), specialmente se vive in un altro modulo. –

+3

Inoltre, menzionerò che, diversamente da onullstream di Neil, il tuo NullStream non può essere passato a una funzione che prevede un argomento ostream & o ostream *. –

+0

expensive_function() sarà _definitamente_ essere valutato indipendentemente da dove vive. Non c'è modo di pervenire che escluda le macro e la compilazione condizionale :) ... per quanto riguarda l'onullstream di Neil, non credo che soddisfi il requisito di "zero formatting overhead" :) –

0

Probabilmente avrete bisogno di qualcosa di più della semplice formattazione del testo e del filtro dei messaggi. Che dire del multithreading?

Implementare la sincronizzazione del filtro e del multithreading come responsabilità di una classe separata.

Tuttavia, la registrazione è un problema non così semplice, e vorrei provare a utilizzare le soluzioni di registrazione esistenti, invece di svilupparne una nuova.

15

Un google veloce ha fornito questo esempio che potrebbe essere utile. Offro garanzie, se non che si compila e funziona :-)

#include <streambuf> 
#include <ostream> 

template <class cT, class traits = std::char_traits<cT> > 
class basic_nullbuf: public std::basic_streambuf<cT, traits> { 
    typename traits::int_type overflow(typename traits::int_type c) 
    { 
     return traits::not_eof(c); // indicate success 
    } 
}; 

template <class cT, class traits = std::char_traits<cT> > 
class basic_onullstream: public std::basic_ostream<cT, traits> { 
    public: 
     basic_onullstream(): 
     std::basic_ios<cT, traits>(&m_sbuf), 
     std::basic_ostream<cT, traits>(&m_sbuf) 
     { 
      init(&m_sbuf); 
     } 

    private: 
     basic_nullbuf<cT, traits> m_sbuf; 
}; 

typedef basic_onullstream<char> onullstream; 
typedef basic_onullstream<wchar_t> wonullstream; 

int main() { 
    onullstream os; 
    os << 666; 
} 
+0

+1. Sì, immagino che derivare da std :: basic_ostream <> sia necessario se vuoi passare un flusso do-nothing in una funzione che prevede un ostream & o un parametro ostream * - Il trucco di Iraimbilanja non funzionerà lì. –

+0

potrebbe essere accettato solo come 'ostream *', non 'ostream &', corrrect? – athos

0

Perché non utilizzare soluzioni di registrazione esistenti utilizzati da milioni di utenti? log4j, log4net, log4cxx .., per citarne solo alcuni ..

13

tutto, grazie per la condivisione del codice, ho solo fare una prova, allora il metodo di Neil sarà ancora fare la formattazione di stringa, per esempio:

#include <streambuf> 
#include <ostream> 
#include <iostream> 
using namespace std; 


template <class cT, class traits = std::char_traits<cT> > 
class basic_nullbuf: public std::basic_streambuf<cT, traits> { 
    typename traits::int_type overflow(typename traits::int_type c) 
    { 
     return traits::not_eof(c); // indicate success 
    } 
}; 

template <class cT, class traits = std::char_traits<cT> > 
class basic_onullstream: public std::basic_ostream<cT, traits> { 
    public: 
     basic_onullstream(): 
     std::basic_ios<cT, traits>(&m_sbuf), 
     std::basic_ostream<cT, traits>(&m_sbuf) 
     { 
      init(&m_sbuf); 
     } 

    private: 
     basic_nullbuf<cT, traits> m_sbuf; 
}; 

typedef basic_onullstream<char> onullstream; 
typedef basic_onullstream<wchar_t> wonullstream; 

class MyClass 
{ 
    int a; 
    friend ostream& operator<< (ostream&, MyClass const&); 
}; 

ostream& operator<<(ostream& out,MyClass const& b) 
{ 
    std::cout<<"call format function!!"; 
    out << b.a; 
    return out; 
} 

int main() { 
    onullstream os; 
    MyClass obj; 
    os<<obj; 
} 

l'esecuzione di questo programma, vi accorgerete che "ostream & operatore < < (ostream & fuori, MyClass const & b)" sarà chiamato. Quindi, il formato sull'oggetto verrà comunque chiamato. Quindi, non possiamo ancora evitare il sovraccarico della formattazione dei messaggi.

+0

AFAIK l'ottimizzatore può rimuovere il codice che non ha effetti collaterali. Dal momento che la tua chiamata a 'std :: cout <<" call format function !! "' ha effetti collaterali, quindi l'ottimizzatore non lo rimuoverà. Tuttavia, senza la chiamata, è possibile che questo verrà rimosso –

Problemi correlati