2010-05-10 17 views
10

Ich versuche, meine eigene Logging-Klasse zu schreiben und es als Stream verwenden:C++ Stream als Parameter, wenn eine Überlastung Operator <<

logger L; 
L << "whatever" << std::endl; 

Dies ist der Code, den ich mit gestartet:

#include <iostream> 

using namespace std; 


class logger{ 
public: 
    template <typename T> 
    friend logger& operator <<(logger& log, const T& value); 
}; 

template <typename T> 
logger& operator <<(logger& log, T const & value) { 
    // Here I'd output the values to a file and stdout, etc. 
    cout << value; 
    return log; 
} 

int main(int argc, char *argv[]) 
{ 
    logger L; 
    L << "hello" << '\n' ; // This works 
    L << "bye" << "alo" << endl; // This doesn't work 
    return 0; 
} 

Aber ich war ein Fehler, wenn ich versuche zu sagen zu kompilieren, dass es für die Betreiber keine Definition war < < (wenn std :: endl):

pruebaLog.cpp:31: error: no match for ‘operator<<’ in ‘operator<< [with T = char [4]](((logger&)((logger*)operator<< [with T = char [4]](((logger&)(& L)), ((const char (&)[4])"bye")))), ((const char (&)[4])"alo")) << std::endl’ 

Also, ich habe versucht, Operator < < zu überlasten, um diese Art von Streams zu akzeptieren, aber es macht mich verrückt. Ich weiß nicht, wie ich es machen soll. Ich habe Loking an, zum Beispiel der Definition von std :: endl am Ostream Header-Datei und geschrieben eine Funktion mit diesem Header:

logger& operator <<(logger& log, const basic_ostream<char,char_traits<char> >& (*s)(basic_ostream<char,char_traits<char> >&)) 

Aber kein Glück. Ich habe das selbe mit Schablonen versucht, anstatt Char direkt zu verwenden, und versuchte auch einfach, "const ostream & os" und nichts zu verwenden.

andere Sache, die nervt sie ist, dass in der Fehlerausgabe, das erste Argument für Betreiber < < Änderungen, manchmal ist es ein Verweis auf einen Zeiger, sieht manchmal wie eine doppelte Referenz ...

+0

möglich Duplikat [std :: endl ist unbekannter Art wenn Betreiber Überlastung <<] (http://stackoverflow.com/questions/1134388/stdendl-is-of-unknown-type-when-overloading-operator) – sth

+1

Gute Frage. Ich bin letzte Woche auf dasselbe Problem gestoßen. Traurige Stimme der Erfahrung in den Antworten unten ... –

+0

@ T.E.D .: Ähm ... danke? –

Antwort

9

endl ist ein seltsames Tier. Es ist kein konstanter Wert. Es ist tatsächlich von allen Dingen eine Funktion. Sie benötigen eine spezielle Überschreibung die Anwendung von endl zu handhaben:

logger& operator<< (logger& log, ostream& (*pf) (ostream&)) 
{ 
    cout << pf; 
    return log; 
} 

Dies nimmt Einfügung einer Funktion, die einen ostream Bezug und gibt einen ostream Bezug nimmt. Das ist, was endl ist.

Edit: Als Reaktion auf FranticPedantic die interessante Frage nach dem „Warum kann nicht der Compiler dies automatisch ableiten?“. Der Grund dafür ist, dass wenn man noch tiefer tauchen, endl selbst eigentlich eine Vorlage Funktion. Es ist wie folgt definiert:

template <class charT, class traits> 
    basic_ostream<charT,traits>& endl (basic_ostream<charT,traits>& os); 

Das heißt, es irgendeine Art von ostream als Eingang und Ausgang nehmen. Das Problem ist also nicht, dass der Compiler nicht ableiten kann, dass T const & ein Funktionszeiger sein könnte, aber dass er nicht herausfinden kann, welchesendl Sie übergeben wollten. Die in der Frage präsentierte Vorlagenversion von operator<< würde akzeptieren ein Zeiger auf eine beliebige Funktion als zweites Argument, aber zugleich die endl Vorlage stellt eine unendlich Menge potentieller Funktionen, so dass der Compiler nicht sinnvoll, dort etwas tun.

die besondere Überlastung der operator<< deren Bereitstellung zweite Argument entspricht eine spezifische Instanziierung der endl Vorlage ermöglicht es der Anruf zu lösen.

+0

Danke! Das hat es gelöst. Jetzt, da ich Ihre Lösung sehe, finde ich, dass meine Definition 'basic_ostream > & (* s) (basic_ostream > &)', die eine generische Version von ostream ist, hat nicht funktioniert weil es "const" definiert wurde. Sobald ich die const fallen gelassen habe, hat es funktioniert. –

+0

Für mich die eigentliche Frage bleibt, weshalb nicht der Compiler T als Funktionszeiger ableiten und daran binden? –

+0

@FranticPedantic Interessanter Punkt! Bearbeitet in der Antwort. –

0

In C++ es ist der Stream-Puffer, der den zugrunde liegenden I/O-Mechanismus einkapselt. Der Stream selbst kapselt nur die Konvertierungen in die Zeichenfolge und die E/A-Richtung.

Daher sollten Sie eine der vordefinierten Stream-Klassen verwenden, anstatt Ihre eigenen zu erstellen. Wenn Sie ein neues Ziel haben, zu dem Ihre E/A gehen soll (wie ein Systemprotokoll), sollten Sie Ihren eigenen Strompuffer (abgeleitet von std::streambuf) erstellen.

5

endl ist ein IO-Manipulator, ein Funktor, der einen Stream per Referenz akzeptiert, einige Operationen daran ausführt und diesen Stream auch als Referenz zurückgibt. cout << endl entspricht cout << '\n' << flush, wobei flush ein Manipulator ist, der den Ausgabepuffer löscht.

In Ihrer Klasse, müssen Sie nur eine Überlastung für diesen Operator schreiben:

logger& operator<<(logger&(*function)(logger&)) { 
    return function(*this); 
} 

Wo logger&(*)(logger&) der Typ einer Funktion zu akzeptieren und ein logger durch Verweis zurück. Um Ihre eigenen Manipulatoren zu schreiben, schreiben Sie einfach eine Funktion, die diese Signatur übereinstimmt, und haben sie eine Operation auf dem Strom durchführen:

logger& newline(logger& L) { 
    return L << '\n'; 
} 
+0

Dies ist komplizierter als die akzeptierte Antwort, aber ein interessanter Ansatz dennoch. –

+0

@FranticPedantic: Zwei Seiten der gleichen Münze, wirklich. –

Verwandte Themen