2013-07-21 29 views
6

Ich habe mein eigenes Objekt std::cout erstellt, das sowohl nach std::cout als auch in eine Protokolldatei schreibt.Richtiger Weg zum Deklarieren/Definieren eines benutzerdefinierten Cout-ähnlichen Objekts

Ich definiere es gerade so in einer Header-Datei, aber ich bekomme ungenutzte variable Warnungen.

Header-Datei <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; 
} 

Verbrauch:

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

lostatic werden sollte? Sollte loextern sein?

Kudos zur Erklärung der korrekten Art und Weise, ein cout-ähnliches Objekt zu deklarieren und zu zeigen, wie es die Haupt-Standardbibliothek-Implementierungen tun.


Edit: von cout -ähnlichen Objekt, meine ich eine globale Variable, die nach einschließlich der entsprechenden Kopf immer verfügbar ist.

+0

'std :: cout' ist in der Regel nur ein' std :: ostream' irgendeiner Art, mit einigen speziellen Logik, um sicherzustellen, dass es früh genug initialisiert und nie zerstört wird; zumindest ein Compiler, den ich kenne, verwendet spezielle Erweiterungen, um dies zu erreichen. Aber du brauchst es normalerweise nicht; Wenn es schlimmer wird, können Sie ein Singleton verwenden und 'log() << ...' schreiben. –

Antwort

5

std::cout wird einfach wie folgt erklärt:

namespace std { 
    extern ostream cout; 
} 

Es ist eine regelmäßige globale Variable; Sie können das Gleiche selbst tun. Setzen Sie eine extern Deklaration Ihrer Variablen in eine Kopfzeile; definieren dann die gleiche Variable in einer Quelldatei und verknüpfen Sie es mit Ihrer Anwendung:

// mylog.h 
extern MyLog mylog; 

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

Danke. Ich hatte versucht, es "external" zu deklarieren, aber vergessen, die Variable in der Quelldatei zu definieren. –

1

Erstens bin ich mir nicht sicher, was Sie meinen, ein cout ähnliches Objekt? Vielleicht ein std::ostream.

Wie auch immer, der übliche Weg, dies zu tun ist, eine Filterung streambuf zu verwenden. Schreiben Sie einfach eine streambuf, die in eine Protokolldatei leitet, zusätzlich zu der üblichen Stelle, und legen Sie sie, wo immer Sie wollen :

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); 
     } 
    } 
}; 

(Dies ist C++ 11, aber es sollte nicht schwer sein, . abzuändern, um sie für C++ 03)

zu verwenden, könnten Sie so etwas wie verwenden:

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

Alle Ausgaben zu std::cout wirderlischt bis logger angemeldet seindes Umfangs.

In der Praxis werden Sie wahrscheinlich etwas komplizierter als ein filebuf für die Protokollierung verwenden, da Sie Zeitstempel am Anfang jeder Zeile oder systematisch bündig am Ende jede Zeile eingefügt werden sollen können. (Filtering streambufs können Pflege dieser Probleme nehmen auch.)

+0

Danke, aber ich wünsche eine globale Variable, die immer verfügbar ist, deshalb habe ich über 'Cout'-ähnliches Objekt gesprochen –

+0

Sie waren also besorgt über die Lebensdauer. Das war nicht klar. In diesem Fall ist die offensichtliche Lösung "std :: ostream & logStream() {statisch std :: ostream" theOneAndOnly = new MyStreamType; return * theOneAndOnly; } 'Es gibt eine leichte Änderung in der Syntax: Sie müssen' logStream() << ... 'schreiben, anstatt' logStream << ... ', aber das ist es auch schon. (Und Sie können immer noch den 'LoggingOutputStreambuf' oben im Stream verwenden, den Sie von 'logStream' zurückgeben; er bleibt die einfachste und eleganteste Art, die Ausgabe an zwei separate Ziele zu senden.) –

0

In einem meiner projects, ich Wrapper für std::cout schrieb.

Es sieht ungefähr so ​​aus:

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

out_t out; 

out << 1; 

Für vollständigen Code sucht struct out in io.h

+0

Das ist näher an dem, was ich will - aber ich möchte eine globale Instanz von out, die ohne Operator() verwendet werden kann, wie 'cout' tut. –

+0

Es gibt nichts, was Sie dazu berechtigt, ein globales "out" -Objekt zu deklarieren. Ich habe den Code bearbeitet, um zu zeigen, wie es geht. –

+0

@LeonidVolnitsky Wenn ich C++ 11 nicht verwende, ist es OK, '&&' durch '&' im angezeigten Code-Snippet zu ersetzen? – synaptik

1

std :: cout-ähnlichen Objekt, das sowohl schreibt std :: cout und in ein Protokoll Datei

Vielleicht boost.iostreams wäre ausreichend?

#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

Ich möchte keine Boost-Abhängigkeit einführen, und ich möchte eine globale Variable wie 'cout' –

1

einfach den Eingangswert zu Recht darauf cout Senden nicht für mich arbeiten, weil ich das Protokoll hinzuzufügen Kopf- und Informationen gesucht Ausgabe.

Auch hatte ich meine statische Debug-Klasse, in der den Log-Stream zu wickeln.

So habe ich es geschafft, ich hoffe es ist nützlich. Ich bin irgendwie ein newbye zu C++, so fühlen sich frei, mir zu sagen, wenn etwas nicht stimmt :)

#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; 

} 
Verwandte Themen