2008-11-14 10 views
5

Original QuestionWie zu überlasten Operator <<, dass nimmt oder nicht zurück Ostream

ich eine Logging-Klasse bin writting, wo das Ziel dieser Lage sein, zu tun:

// thread one 
Logger() << "Some string" << std::ios::hex << 45; 
// thread two 
Logger() << L"Some wide string" << std::endl; 

Aktuell mein Logger Header etwas wie folgt aussieht:

#pragma once; 
#include <ostream>  
class Logger 
{ 
public: 
    Logger(); 
    ~Logger(); 

    std::ostream* out_stream; 
}; 

template <typename T> 
Logger& operator<< (Logger& logger, T thing) { 
    *logger.out_stream << thing; 
    return logger; 
} 

Einige Anmerkungen zu dieser Klasse:

  1. Plattformübergreifende Kompatibilität ist kein Problem.
  2. Innerhalb von Logger.cpp gibt es eine Singleton-Klasse, die für die Erstellung der "echten" Ostream sorgt.
  3. Der Logger-Konstruktor und Dekonstruktor führen die erforderliche Verriegelung des Singleton durch.

Ich habe drei Probleme:

  • Wie kann ich den Bediener < < Funktion ein Freund oder ein Mitglied, so kann ich out_stream als privat?
  • Wie mache ich den Operator < < Funktion arbeiten für Manipulatoren?
  • Wie kann ich eine Spezialisierung hinzufügen, so dass, wenn T ein WCHAR * oder std :: wstring ist, es in char * oder std :: string konvertiert wird, bevor es an out_stream übergeben wird? (Ich kann die Umwandlung tun verlieren hohe Unicode-Zeichen ist kein Problem in meinem Fall..)

Zusammenfassung der Dinge in Antworten gelernt:

  • Put Vorlage VOR Freund stattdessen nach der.
  • std :: ios :: hex ist kein Manipulator. std :: hex ist ein Manipulator.

End Ergebnis

#pragma once 
#include <ostream> 
#include <string> 

std::string ConvertWstringToString(std::wstring wstr); 

class Logger 
{ 
public: 
    Logger(); 
    ~Logger(); 

    template <typename T> 
    Logger& operator<< (T data) { 
     *out << data; 
     return *this; 
    } 
    Logger& operator<< (std::wstring data) { 
     return *this << ConvertWstringToString(data); 
    } 
    Logger& operator<< (const wchar_t* data) { 
     std::wstring str(data); 
     return *this << str; 
    } 

private: 
    std::ostream* out; 
}; 
+0

Ihr Endergebnis ist falsch. Spezialisierungen im Klassenbereich sind nicht erlaubt :) einfach überladen sie stattdessen (das Template <> Teil weglassen) Es ist erforderlich in Adams Antwort (die sie auf Namespace-Bereich spezialisiert), da sonst die (dann normalen Operatorfunktionen) keine Freunde mehr sind. –

+0

Interessanterweise hat es dort mit ihnen funktioniert. Aber um richtig zu sein, habe ich sie trotzdem entfernt. Vielen Dank! –

Antwort

7

Sie verwenden Freund arbeiten Definition, die die Bediener in dem umgebenden Namensraum der Klasse definieren, und es nur an Betreibern Überlastung Auflösung (nicht manuell aufrufbar mit dem Operator :: < < ... Syntax) sichtbar machen:

Die Alternative, halten Sie Ihren Code wie es ist, und macht nur den Betreiber < < Vorlage einen Freund, Sie diese Zeile in Ihre Klassendefinition hinzufügen:

template <typename T> 
friend Logger& operator<< (Logger& logger, T thing); 

Für das Manipulator Problem, werde ich nur geben Sie meinen Code, den ich vor einiger Zeit schreibe:

#include <iostream> 
#include <cstdlib> 
using namespace std; 

template<typename Char, typename Traits = char_traits<Char> > 
struct logger{ 
    typedef std::basic_ostream<Char, Traits> ostream_type; 
    typedef ostream_type& (*manip_type)(ostream_type&); 
    logger(ostream_type& os):os(os){} 
    logger &operator<<(manip_type pfn) { 
     if(pfn == static_cast<manip_type>(std::endl)) { 
      time_t t = time(0); 
      os << " --- " << ctime(&t) << pfn; 
     } else 
      os << pfn; 
     return *this; 
    } 
    template<typename T> 
    logger &operator<<(T const& t) { 
     os << t; 
     return *this; 
    } 
private:   
    ostream_type & os; 
}; 

namespace { logger<char> clogged(cout); } 
int main() { clogged << "something with log functionality" << std::endl; } 

};

Beachten Sie, dass es std :: hex ist, aber nicht std :: ios :: hex. Letzteres wird als Manipulator-Flag für die setf-Funktion von Streams verwendet. Beachten Sie, dass für Ihr Beispiel keine spezielle Behandlung von Manipulatoren erforderlich ist. Die obige spezielle Behandlung von std :: endl wird nur benötigt, weil ich die Zeit zusätzlich streame, wenn std :: endl benutzt wird.

+0

Argh! Kein Wunder, dass ich 204845 im Log bekommen habe, als ich das gemacht habe. Es sieht so aus, als ob Manipulatoren funktionieren wie sie sind. Vielen Dank! –

0

, warum es die printf Art und Weise nicht tun und die Multi-Parameter-Methode verwenden (mit den drei Punkten ...). Dies gibt Ihnen immer noch viel Formatierungskraft und macht es nicht so unordentlich, wie wenn Sie die < < verwenden.

Zum Beispiel:

Logger("This is my log msg %0X", 45); 

Hang auf zwei Sekunden und krank ein Codebeispiel für Sie nach oben ziehen.

Edit:

void Logger(const char* format, ...) 
{ 
    char szMsg[3000]; 

    va_list args; 
    va_start(args, format); 
    vsnprintf(szMsg, sizeof(szMsg) - 1, format, args); 
    va_end(args); 

    // code to print szMsg to a file or whatever here 
} 

Wenn Sie dies als eine Klasse keinen Stand-alone-Funktion verwenden möchten können Sie die Logger Betreiber überlasten() und es wird können nur den gleichen

+0

Sehr guter Vorschlag! Es gibt zwei Gründe, warum ich das << Format verwenden möchte. Einer ist, dass ich die Lesbarkeit von Verkettungen bevorzugen. Die andere ist, dass ich die Erstellung von vielen Char-Puffern vermeiden möchte. –

+0

Es gibt eine Reihe von Gründen, die varargs-Methode in C++ - Code nicht zu verwenden, insbesondere die Typsicherheit zu entfernen, eine enge Kopplung zwischen Aufrufer/Aufrufer und undefiniertem Verhalten von UDTs (auch wenn sie noch erlaubt sind). – twokats

+0

Gut zu wissen. Nie war ein Fan von << in C++ – Lodle

2

Verwendung einer Vorlage ist der richtige Weg, es zu tun, aber Sie müssen nur sicherstellen, dass die Vorlage im Header ist Datei (logger.h, oder was auch immer Sie genannt), nicht in der Implementierungsdatei (logger.cpp) . Dies funktioniert automatisch für jeden Typ, der operator << mit einem std::ostream definiert hat. Es wird auch automatisch mit Stream-Manipulator-Objekten arbeiten - das sind wirklich nur Funktionen, die einen std::ostream Parameter annehmen, und operator << ruft nur die Funktion auf ostream auf.

können Sie machen operator << eine Friend-Funktion wie folgt:

template <typename T> friend Logger& operator<< (Logger& logger, T thing); 

Spezialisierungen sind einfach - Vorlage Spezialisierungen verwenden (auch hier in der Header-Datei):

template <typename T> 
Logger& operator<< (Logger& logger, T thing) { 
    *logger.out_stream << thing; 
    return logger; 
} 

// Template specialization - the "template <>" part is necessary 
template <> 
Logger& operator<< (Logger& logger, const wchar_t *wstr) 
{ 
    // convert wstr to an ANSI string and log it 
} 

template <> 
Logger& operator<< (Logger& logger, const std::wstring & wstr) 
{ 
    // convert wstr to an ANSI string and log it 
} 
2

Keine Freundschaft Erklärung erforderlich:

class Logger 
{ 
public: 
    Logger(); 
    ~Logger(); 

template <typename T> 
inline Logger& Display(T thing) 
{ 
    *out_stream << thing; 
    return *this; 
} 
private: 
    std::ostream* out_stream; 
}; 

template <typename T> 
Logger& operator<< (Logger& logger, T thing) 
{ 
    return logger.Display(thing); 
}