cout / clog klasse



  • Ist es unangebracht zu fragen, warum meine Lösung, obgleich natürlich nicht so schön von streambuf ableitend und ostream nutzend, nicht gut ist, obwohl sie zu funktionieren scheint, obwohl das hier nicht mein Thread ist? 🙂



  • Eisflamme schrieb:

    Ist es unangebracht zu fragen, warum meine Lösung [...] nicht gut ist [...]? 🙂

    Nein. Aber dein und Werner Salomons Code tun etwas Verschiedenes. Während man bei dir die Verzweigung über ein Objekt deiner Klasse erreicht, zapft Werner direkt std::cout an, sodass der Benutzer weiterhin

    std::cout << xy;
    

    schreiben kann.

    Und ich würde beim operator<< eine Const-Referenz auf T als Parameter angeben.



  • Wieso kann der denn bei mir nicht cout << xy; schreiben?


  • Administrator

    Eisflamme schrieb:

    Wieso kann der denn bei mir nicht cout << xy; schreiben?

    Kann er schon, aber wenn er mit deiner Lösung std::cout << xy; schreibt, dann geht dies nur an die Konsole und nicht auch noch ins File. Mit der Lösung von Werner Salomon kann er std::cout << xy; schreiben und es geht gleichzeitig ins File und auf die Konsole. Schau dir den Code doch einfach mal an 😉

    Das praktische an der Lösung von Werner Salomon ist, dass man die Weiche an einen Scope binden kann und auch auf andere Stream-Objekte angewendet werden kann. Man kann ohne Probleme die Weiche auch auf std::clog anwenden.

    Grüssli



  • Ich hatte den Code überflogen und für ein richtiges Verständnis noch viel Recherchearbeit gesehen. Ich schau mir den nochmal ausführlicher an, danke 🙂



  • hm der code von Werner Salomon klappt sehr gut und ist auch das, was ich will. Hier auch noch ein dankeschön an Eisflamme (auch wenn es nicht das war, wonach ich gesucht habe).
    Ich versteh den code von Wernen kein bisschen, jedoch würde ich gerne z.b. bei einem cout von 2 (cout << 2 << endl;) folgendes in die datei schreiben:
    [DATUM:UHRZEIT] 2

    da ich keine ahnung habe wie ich das im vorliegenden code umsetze wollte ich hier dazu nochmal einen rat.



  • Dann steck das was du in die Datei packen willst da auch rein ...

    Diese Weiche "kopiert" die Eingabe einfach einmal und gibts an 2 weiter ... als ob ein Tastendruck der Tastatur nicht nur ein echo auf dem Bildschirm auslöst sondern gleichzeitig auch noch den Buchstaben auf dem Drucker ausgibt ...



  • prog.exe 1>log.txt
    

    😃



  • Also ich weiss nicht ob mein Beitrag Dir weiter hilft, aber ich versuchs mal.
    Ich hab mir vor einiger Zeit selbst eine Logger-Klasse geschrieben, welche verschiedene Arten von Outputs zulässt, da der "Writer" eine virtuelle Klasse ist, welche man implementieren kann. Es wäre ein einfaches in den bestehenden Code einfach ein "cout << error" einzubauen.
    Das wäre die einfachste Lösung.
    Hier die Logger-Klasse selbst, NLLog:
    http://code.google.com/p/nightlighttv/source/browse/NightLight/trunk/NightLightDLL/NLLog.cpp

    Der HTML-Writer:
    http://code.google.com/p/nightlighttv/source/browse/NightLight/trunk/NightLightDLL/NLHtmlWriter.cpp

    Die virtuelle Klasse:
    http://code.google.com/p/nightlighttv/source/browse/NightLight/trunk/NightLightDLL/NLILogWriter.hpp

    Wenn man jetzt die Funktion die das ganze an den Writer gibt so abändert:

    void NLLog::printToLog(NLLogLevel lvl, const std::string message)
    {
        if ( !m_stream.is_open() || m_writer == NULL ) {
            return; 
        }
        switch(lvl)
        {
        case LL_MESSAGE:
            {
                if ( m_level >= LL_MESSAGE )
                {
                    this->printMessage(message);
                    cout << "[Message] " << message << endl;
                }
                break;
            }
        case LL_WARNING:
            {
                if ( m_level >= LL_WARNING )
                {
                    this->printWarning(message);
                    cout << "[Warning] " << message << endl;
                }
                break;
            }
        case LL_ERROR:
            {
                if ( m_level >= LL_ERROR )
                {
                    this->printError(message);
                    cout << "[Error] " << message << endl;
                }
                break;
            }
        case LL_VERBOSE:
            {
                if ( m_level >= LL_VERBOSE )
                {
                    this->printVerbose(message);
                    cout << "[Verbose] " << message << endl;
                }
                break;
            }
        default:
            {
                this->printError("Unknown LogLevel for message: " + message);
                break;
            }
        }
        m_stream.flush();
    }
    

    Das wäre die einfachste Methode und für Dich vlt auch verständlicher.
    Ich hoffe es hilft.
    rya.



  • Krauzi schrieb:

    hm der code von Werner Salomon klappt sehr gut und ist auch das, was ich will.

    .. das ist ja prima, obwohl er einige kleine Fehler enthält und schon 5 Jahre alt ist.

    Krauzi schrieb:

    Ich versteh den code von Wernen kein bisschen, jedoch würde ich gerne z.b. bei einem cout von 2 (cout << 2 << endl;) folgendes in die datei schreiben:
    [DATUM:UHRZEIT] 2

    da ich keine ahnung habe wie ich das im vorliegenden code umsetze wollte ich hier dazu nochmal einen rat.

    man kann diese Funktionalität natürlich mit in die Weiche hinein programmieren, aber schöner ist es, wenn man noch einen weiteren Filter schreibt, den man sozusagen 'vor' die Dateiausgabe hängt, der den Timestamp dann vor jede Zeile setzt. Mit Standard Mitteln schreibe man sich wieder eine von std::streambuf abgeleitet Klasse, die vor jedes Zeichen, welches einem Zeilenende folgt, den Zeitstempel schreibt. Das sähe so aus:

    #include <iostream>
    #include <cassert>
    
    class TimeStamp : public std::streambuf // oder: std::basic_streambuf< wchar_t >
    {
    public:
        TimeStamp( std::basic_ostream< char_type >& target )
            : m_target( target )
            , m_newline( true )
        {}
    
    protected:
        virtual int_type overflow( int_type m = traits_type::eof() )
        {
            const char_type LF('\n');
            if( traits_type::eq_int_type( m, traits_type::eof() ) )
                return sync() == 0? traits_type::not_eof( m ): traits_type::eof();
            if( m_newline )
            {
                // --   Zeilenanfang -> Timestamp ausgeben
                m_newline = false;
                m_target << now << " ";  // siehe "time_put.h"
            }
            const char_type c = traits_type::to_char_type( m );
            if( c == LF )
                m_newline = true;
            assert( m_target.rdbuf() );
            return m_target.rdbuf()->sputc( c ) != traits_type::eof()? m: traits_type::eof();
        }
        int sync()
        {
            return m_target.flush()? 0: -1;
        }
    private:
        std::basic_ostream< char_type >& m_target;
        bool m_newline;
    };
    

    Der Timestamp wird in Zeile 22 von einem sogenannten Manipulator 'now' geschrieben, etwas in ähnlicher Form gab es schon mal hier.
    Eine etwas modernere Version lege ich hier bei

    // ---   Datei 'time_put.h'
    #include <iostream>
    #include <locale>               // std::time_put<>
    #include <ctime>                // std::tm usw.
    
    template< class charT, class traits >
    std::basic_ostream< charT, traits >& operator<<( std::basic_ostream< charT,traits >& out,
               const std::tm& dateTime )
    {
        typename std::basic_ostream< charT, traits >::sentry cerberos( out );
        if( cerberos )
        {
            // Bem. Pattern-Format siehe: <http://www.cplusplus.com/reference/clibrary/ctime/strftime>
            const charT pattern[] = "[%x %H:%M:%S]";      // [Datum Stunde:Minute:Sekunde]
    //        const charT pattern[] = "%x %H:%M:%S";      // Datum Stunde:Minute:Sekunde
    //        const charT pattern[] = "%H:%M:%S";      // Stunde:Minute:Sekunde
            const charT* patternEnd = pattern + (sizeof(pattern)/sizeof(pattern[0])) - 1;
    
            typedef std::ostreambuf_iterator< charT, traits > outIter_t;
            std::use_facet< std::time_put< charT, outIter_t > >( out.getloc() )
                .put( outIter_t( out ), out, out.fill(), &dateTime, pattern, patternEnd );
        }
        return out;
    }
    
    template< class charT, class traits >
    std::basic_ostream< charT, traits >& now( std::basic_ostream< charT, traits >& out )
    {
        const std::time_t jetzt_ = std::time( static_cast< std::time_t* >( 0 ) );
        return out << *std::localtime( &jetzt_ );
    }
    

    das Hauptprogramm sieht dann änhlich wie das aus, wie bei der 'Weiche', nur der Timestamp kommt noch hinzu.

    int main()
    {
        using namespace std;
        ofstream fout("cout.log");
        TimeStamp ts( fout ); // Timestamp nutzt jetzt 'fout' als Ausgabedevice
        Weiche weiche( cout, &ts ); // die leicht variierte Weiche, nach cout und nach TimeStamp
    
        cout << 2 << endl;
        cout << "naechste Zeile" << endl;
        return 0;
    }
    

    .. und wenn Du das Datum in deutsch haben möchtest, so füge hinter Zeile 4 noch

    fout.imbue( locale("german") );
    

    ein. Der String "german" gilt für Visual Studio; in anderen Umgebungen kann er auch anders lauten.

    Und zum Schluss nochmal die überarbeitete Weiche - im wesentlichen hat der Konstruktor eine andere Signatur bekommen.

    #include // .. wie oben
    class Weiche : public std::streambuf
    {
    public:
        typedef std::streambuf base_type;
        typedef base_type::traits_type traits_type;
    
        Weiche( std::ostream& out, std::streambuf* sb2 )
            : m_sb1( out.rdbuf() )
            , m_sb2( sb2 )
            , m_out1( out )
        {
            out.rdbuf( this );  // hänge mich selbst bei 'out' als Streambuf ein.
        }
    
        ~Weiche()
        {
            m_out1.rdbuf( m_sb1 ); // Stellt die Ausgangssituation wieder her
        }
    
    protected:
        virtual int_type overflow( int_type m = traits_type::eof() )
        {   // jedes Zeichen an BEIDE Streambufs sb1 & sb2 weitergeben
            assert( m_sb1 && m_sb2 );
            if( traits_type::eq_int_type( m, traits_type::eof() ) )
                return sync() == 0? traits_type::not_eof( m ): traits_type::eof();
            const char_type c = traits_type::to_char_type( m );
            if(     !traits_type::eq_int_type( m_sb1->sputc( c ), traits_type::eof() )
                &&  !traits_type::eq_int_type( m_sb2->sputc( c ), traits_type::eof() )
                )
                return m;
            return traits_type::eof();  // mindestens einer der Streambufs lieferte einen Fehler
        }
        int sync()
        {
            return m_sb1->pubsync() == 0 && m_sb2->pubsync() == 0? 0: -1;
        }
    private:
        Weiche( const Weiche& );
        Weiche& operator=( const Weiche& ); // Kopieren verhindern
    
        std::streambuf* m_sb1;
        std::streambuf* m_sb2;
        std::ostream& m_out1;
    };
    

    Die Ausgabe in die Datei sollte dann sein:

    [09/14/10 23:32:46] 2
    [09/14/10 23:32:46] naechste Zeile
    

    der Vorteil dieser Konstruktion ist, dass man TimeStamp und Weiche auch unabhängig von einander nutzen kann.

    Gruß
    Werner



  • :: zieht den Hut ::: und gratuliert: Werner Salomon Mitglied Beiträge: 1500


Anmelden zum Antworten