Frage

Originalfrage

Ich schreibe eine Protokollierungsklasse, in der das Ziel darin besteht, dies zu tun:

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

Derzeit sieht mein Logger -Header so aus:

#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 Notizen zu dieser Klasse:

  1. Die Kompatibilität der Cross -Plattform ist kein Problem.
  2. Innerhalb von Logger.cpp befindet sich eine Singleton -Klasse, die sich darum kümmert, den "echten" Ostream zu erstellen.
  3. Der Logger -Konstruktor und der Dekonstruktor führen die notwendige Verriegelung des Singleton durch.

Ich habe drei Probleme:

  • Wie mache ich den Bediener << Funktion zu einem Freund oder Mitglied, damit ich als privat ausstatten kann?
  • Wie mache ich den Operator << Funktion für Manipulatoren?
  • Wie kann ich eine Spezialisierung hinzufügen, damit wenn t ein wchar* oder std :: wstring ist, dass es sie in char* oder std :: string umwandelt, bevor sie an out_stream übergeben wird? (Ich kann die Konvertierung durchführen. In meinem Fall ist es kein Problem, hohe Unicode -Zeichen zu verlieren.)

Zusammenfassung der in Antworten gelernten Dinge:

  • Setzen Sie die Vorlage vor Freund statt nach nach.
  • std :: ios :: hex ist kein Manipulator. STD :: Hex ist ein Manipulator.

Endresultat

#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;
};
War es hilfreich?

Lösung

Sie können Friend Definition verwenden, die den Bediener im umgebenden Namespace der Klasse definiert und ihn nur für die Überlastung des Bedieners sichtbar macht (nicht manuell mit dem :: Operator << ... Syntax):

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

    std::ostream* out_stream;

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

    /* special treatment for std::wstring. just overload the operator! No need
     * to specialize it. */
    friend Logger& operator<< (Logger& logger, const std::wstring & wstr) {
        /* do something here */
    }

};

Die Alternative, um Ihren Code so zu halten, wie er ist, und einfach den Bediener << Vorlage zu einem Freund zu machen, fügen Sie diese Zeile Ihrer Klassendefinition hinzu:

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

Für das Manipulatorproblem werde ich Ihnen nur meinen Code geben, 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 verwendet setf Funktion von Streams. Beachten Sie, dass für Ihr Beispiel keine besondere Behandlung von Manipulatoren erforderlich ist. Die obige Sonderbehandlung von std :: endl wird nur benötigt, da ich die Zeit zusätzlich streamen, wenn STD :: endl verwendet wird.

Andere Tipps

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

Du kannst das schaffen operator << Eine Freundschaftsfunktion wie folgt:

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

Spezialisierungen sind einfach - Verwenden Sie einfach Vorlagenspezialisierungen (erneut 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
}

Keine Freundschaftserklä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);
}

Warum tun Sie es nicht auf die Drucke und verwenden Sie die Multi -Parameter -Methode (mit den drei Punkten ...). Dies gibt Ihnen immer noch eine Menge an Kraft und macht es nicht so chaotisch wie bei der Verwendung des <<.

Zum Beispiel:

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

Halten Sie zwei Sekunden fest und ziehen Sie ein Code -Beispiel für Sie hoch.

Bearbeiten:

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 diese als Klasse nicht als eigenständiger Funktion verwenden möchten, können Sie den Logger -Operator () überlasten () und dies funktioniert genauso

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top