2009-02-04 8 views
10

Ich habe eine funktionierende Logger-Klasse, die etwas Text in eine Richtextbox (Win32, C++) ausgibt. Problem ist, ich am Ende immer es wie folgt aus:Wie verwende ich meine Logging-Klasse wie einen C++ - Standardstrom?

stringstream ss; 
ss << someInt << someString; 
debugLogger.log(ss.str()); 

statt, wäre es viel bequemer, es wie ein Strom zu verwenden, wie in:

debugLogger << someInt << someString; 

Gibt es einen besseren Weg, als alles an eine interne Stringstream-Instanz weiterleiten? Wenn ich das tun würde, wann müsste ich spülen?

Antwort

28

Sie müssen operator << entsprechend für Ihre Klasse implementieren. Das allgemeine Muster sieht wie folgt aus:

template <typename T> 
logger& operator <<(logger& log, T const& value) { 
    log.your_stringstream << value; 
    return log; 
} 

Beachten Sie, dass diese befasst sich mit (nicht const) Referenzen da der Betrieb Logger modifiziert. Beachten Sie auch, dass Sie die log Parameter, um für die Verkettung zur Arbeit zurückzukehren müssen:

log << 1 << 2 << endl; 
// is the same as: 
((log << 1) << 2) << endl; 

Wenn die innerste Betrieb nicht die aktuelle log Instanz zurückkehren, werden alle anderen Operationen entweder zum Zeitpunkt der Kompilierung (falsche Methode versagen Signatur) oder zur Laufzeit verschluckt werden.

+0

Wie würde ich mit dem "Flush-Problem" umgehen, d. H. An einem Punkt muss ich alles in meine Richtextbox schicken. Soll ich den eingehenden Wert für "endline" testen? – newgre

+0

Dies ist eine separate Frage, aber die Antwort ist einfach: anstelle von '\ n' verwenden Sie endl! Dadurch wird der Stream automatisch gelöscht. Um jedoch eine TextBox mit einem Stream zu verbinden, müssen Sie im Grunde einen eigenen Strompuffer implementieren (sehen Sie sich die rdbuf-Methode an). –

+1

Oder fügen Sie Ihren eigenen Manipulatortyp hinzu, der endl/endet, mit einer Überladung des Operators <<, um ihn zu erkennen. – Kylotan

1

Überschreiben Sie in der Logger-Klasse den Operator < <.

Klicken Sie auf Here, um zu wissen, wie Sie den Operator < < implementieren können.

Sie können auch die Protokollierungsanweisungen im Code mithilfe der aspektorientierten Programmierung vermeiden.

+2

Ich erhalte einen 404 auf diesen Link. –

14

Überladen der Insertion-Operator < < ist nicht der Weg zu gehen. Sie müssen Überladungen für alle endl oder andere benutzerdefinierte Funktionen hinzufügen.

Der Weg zu gehen ist, definieren Sie Ihre eigenen streambuf, und binden Sie es in einen Stream. Dann müssen Sie nur den Stream verwenden.

Hier sind ein paar einfache Beispiele:

+0

_ "Sie müssen Überladungen für alle endl oder andere benutzerdefinierte Funktionen hinzufügen." _ Das ist eine Überladung. –

+0

@LightnessRacesinOrbit Das ist mehr als eine Überladung, weil wir uns um 'endl',' flush', 'hex',' setw' und so weiter kümmern müssen. Am Ende müssen mehrere Überladungen definiert werden. Wir können die Anzahl der Überlastungen dank Vorlagen reduzieren, aber das ist nicht der beste Ansatz hier. –

+0

Nein, für alle ist eine Überladung erforderlich. Die Standardbibliothek ist so konzipiert. –

0

Als Luc Hermitte noted gibt es "Logging In C++" Artikel, der sehr gepflegte Ansatz beschreibt, um dieses Problem zu lösen. Auf den Punkt gebracht, da haben Sie eine Funktion wie folgt aus:

void LogFunction(const std::string& str) { 
    // write to socket, file, console, e.t.c 
    std::cout << str << std::endl; 
} 

es möglich ist, einen Wrapper zu schreiben es in std :: cout zu verwenden wie Art und Weise:

#include <sstream> 
#include <functional> 

#define LOG(loggingFuntion) \ 
    Log(loggingFuntion).GetStream() 

class Log { 
    using LogFunctionType = std::function<void(const std::string&)>; 

public: 
    explicit Log(LogFunctionType logFunction) : m_logFunction(std::move(logFunction)) { } 
    std::ostringstream& GetStream() { return m_stringStream; } 
    ~Log() { m_logFunction(m_stringStream.str()); } 

private: 
    std::ostringstream m_stringStream; 
    LogFunctionType m_logFunction; 
}; 

int main() { 
    LOG(LogFunction) << "some string " << 5 << " smth"; 
} 

(online demo)

Auch gibt es sehr schöne solution von Stewart zur Verfügung gestellt.

0

Eine elegante Lösung, die auch die Spülung Probleme löst, ist die folgende:

#include <string> 
#include <memory> 
#include <sstream> 
#include <iostream> 

class Logger 
{ 
    using Stream = std::ostringstream; 
    using Buffer_p = std::unique_ptr<Stream, std::function<void(Stream*)>>; 

public: 
    void log(const std::string& cmd) { 
     std::cout << "INFO: " << cmd << std::endl; 
    } 

    Buffer_p log() { 
     return Buffer_p(new Stream, [&](Stream* st) { 
      log(st->str()); 
     }); 
    } 
}; 

#define LOG(instance) *(instance.log()) 

int main() 
{ 
    Logger logger; 
    LOG(logger) << "e.g. Log a number: " << 3; 
    return 0; 
}