2013-07-21 11 views
6

Ho creato il proprio oggetto std::cout -like che scrive sia su std::cout che su un file di registro.Modo corretto per dichiarare/definire oggetto cout-like personalizzato

Attualmente sto definendolo in questo modo in un file di intestazione, ma sto ricevendo avvisi di variabili inutilizzate.

file di intestazione <MyLib/Log.h>

static LOut { }; 
static LOut lo; 

template<typename T> inline LOut& operator<<(LOut& mLOut, const T& mValue) 
{ 
    std::string str{toStr(mValue)}; 
    std::cout << str; 
    getLogStream() << str; 
    return mLOut; 
} 

Usage:

#include <MyLib/Log.h> 
... 
lo << "hello!" << std::endl; 

Dovrebbe essere lostatic? lo dovrebbe essere extern?

Complimenti per aver spiegato il modo corretto di dichiarare un oggetto-cout -like e mostrare come le principali implementazioni della libreria standard lo fanno.


Edit: da cout -come oggetto, intendo una variabile globale che è sempre disponibile dopo inclusa l'intestazione corrispondente.

+0

'std :: cout' è tipicamente solo un' std :: ostream' di qualche tipo, con un po 'speciale logica per garantire che sia inizializzato abbastanza presto, e mai distrutto; almeno un compilatore che conosco usa estensioni speciali per raggiungere questo obiettivo. Ma di solito non ne hai bisogno; se peggiora la situazione, puoi usare un singleton e scrivere 'log() << ...'. –

risposta

5

std::cout è semplicemente dichiarato come segue:

namespace std { 
    extern ostream cout; 
} 

Si tratta di una variabile globale regolare; puoi fare la stessa cosa da solo. Metti una dichiarazione extern della tua variabile in un'intestazione; quindi definire la stessa variabile in un file di origine e collegarlo alla vostra applicazione:

// mylog.h 
extern MyLog mylog; 

// mylog.cpp 
MyLog mylog(someparams); 
+0

Grazie. Ho provato a dichiararlo 'external' ma ho dimenticato di definire la variabile nel file sorgente. –

1

In primo luogo, non sono troppo sicuro di cosa intendi essere un oggetto simile a cout? Forse uno std::ostream.

In ogni caso, il modo usuale per fare ciò è usare un filtro streambuf. Basta scrivere uno streambuf che inoltra ad un file di log, oltre al solito posto, e inserirlo dove mai si mancanza:

class LoggingOutputStreambuf : public std::streambuf 
{ 
    std::streambuf* myDest; 
    std::ofstreambuf myLogFile; 
    std::ostream* myOwner; 
protected: 
    int overflow(int ch) 
    { 
     myLogFile.sputc(ch); // ignores errors... 
     return myDest->sputc(ch); 
    } 
public: 
    LoggingOutputStreambuf(
      std::streambuf* dest, 
      std::string const& logfileName) 
     : myDest(dest) 
     , myLogFile(logfileName.c_str(), std::ios_base::out) 
     , myOwner(nullptr) 
    { 
     if (!myLogFile.is_open()) { 
      // Some error handling... 
     } 
    } 
    LoggingOutputStreambuf(
      std::ostream& dest, 
      std::string const& logfileName) 
     : LoggingOutputStreambuf(dest.rdbuf(), logfileName) 
    { 
     dest.rdbuf(this); 
     myOwner = &dest; 
    } 
    ~LoggingOutputStreambuf() 
    { 
     if (myOwner != nullptr) { 
      myOwner->rdbuf(myDest); 
     } 
    } 
}; 

(Questo è C++ 11, ma non dovrebbe essere difficile . modificarlo per C++ 03)

per utilizzare, si potrebbe usare qualcosa come:

LoggingOutputStreambuf logger(std::cout); 
// ... 

Tutto uscita std::cout sarà registrata fino logger si spegne di scopo.

In pratica, è probabile che usare qualcosa di più complicato di un filebuf per la registrazione, dal momento che si può decidere di inserire data e ora all'inizio di ogni riga, o sistematicamente a filo alla fine di ogni riga . (Il filtraggio degli streambuf può anche occuparsi di questi problemi )

+0

Grazie, ma desidero una variabile globale che sia sempre disponibile, è per questo che ho parlato dell'oggetto 'cout' –

+0

Quindi eri preoccupato della durata. Non era chiaro. In tal caso, la soluzione più ovvia è 'std :: ostream & logStream() {statico std :: ostream * theOneAndOnly = new MyStreamType; return * theOneAndOnly; } 'C'è una leggera modifica nella sintassi: devi scrivere' logStream() << ... ', piuttosto che' logStream << ... ', ma questo è tutto. (E si può ancora usare 'LoggingOutputStreambuf' sopra nello stream che si restituisce da' logStream', rimane il modo più semplice ed elegante per inviare l'output a due destinazioni separate.) –

0

In uno dei miei projects, ho scritto wrapper per std::cout.

Sembra qualcosa di simile a questo:

struct out_t { 
    template<typename T> 
    out_t& 
    operator << (T&& x) { 
      std::cout << x; 
      // log << x; 
      return *this; 
    }; 
}; 

out_t out; 

out << 1; 

Per guardare il codice completo per struct out in io.h

+0

Questo è più vicino a quello che voglio - tuttavia io vuoi un'istanza globale di out che possa essere usata senza operator(), come fa 'cout'. –

+0

Non c'è nulla che ti privets per dichiarare oggetto globale 'out'. Ho modificato il codice per mostrare come farlo. –

+0

@LeonidVolnitsky Se non sto usando C++ 11, è corretto sostituire '&&' con '&' nel frammento di codice che hai visualizzato? – synaptik

1

std :: cout oggetto simile che scrive sia per std :: cout e a un registro file

Forse boost.iostreams potrebbe essere sufficiente?

#include <iostream> 
#include <fstream> 
#include <boost/iostreams/stream.hpp> 
#include <boost/iostreams/tee.hpp> 

namespace io = boost::iostreams; 
int main() 
{ 
    typedef io::tee_device<std::ostream, std::ofstream> teedev; 
    typedef io::stream<teedev> LOut; 
    std::ofstream outfile("test.txt"); 
    teedev logtee(std::cout, outfile); 
    LOut mLOut(logtee); 
    mLOut << "hello!" << std::endl; 
} 
+0

Non voglio introdurre alcuna dipendenza di boost e voglio una variabile globale come 'cout' –

1

semplicemente inviando il valore di ingresso a destra verso cout non ha funzionato per me, perché ho voluto aggiungere intestazioni e informazioni per il registro produzione.

Inoltre, ho avuto la mia classe debug statica in cui avvolgere il flusso di log.

Questo è il modo in cui sono riuscito a farlo, spero sia utile. Sono in qualche modo un newbye a C++, quindi sentitevi liberi di dire a me se qualcosa non va :)

#include <iostream> 
#include <sstream> 
#include <ostream> 

enum class DebugLevel { 
    INFO, 
    WARNING, 
    ERROR 
}; 

class Debug { 

    public: 

     /* other Debug class methods/properties 
      ... 
     */ 

     // out stream object 
     static struct OutStream { 

       std::ostringstream stream; 
       DebugLevel level = DebugLevel::INFO; 

      public: 

       // here you can add parameters to the object, every line log 
       OutStream& operator()(DebugLevel l) { 
        level = l; 
        return *this; 
       } 

       // this overload receive the single values to append via << 
       template<typename T> 
       OutStream& operator<<(T&& value) { 
        stream << value; 
        return *this; 
       } 

       // this overload intercept std::endl, to flush the stream and send all to std::cout 
       OutStream& operator<<(std::ostream& (*os)(std::ostream&)) { 

        // here you can build the real console log line, add colors and infos, or even write out to a log file 
        std::cout << __TIME__ << " [" << (int)level << "] " << stream.str() << os; 

        stream.str(""); // reset the string stream 
        level = DebugLevel::INFO; // reset the level to info 
        return *this; 
       } 

     } Log; 

}; 

Debug::OutStream Debug::Log; // need to be instantiaded only because I use a static Debug class 

int main() { 

    Debug::Log(DebugLevel::ERROR) << "Hello Log! " << 2 << " " << __FUNCTION__ << std::endl; 

    Debug::Log << "Hello Log! " << 0xFA << std::endl; // NB: this way the debugLevel is default 

    return 0; 

} 
Problemi correlati