Von streambuf erben



  • Hallo,

    Ich habe eine Klasse realisiert, auf welche man mit Hilfe des <<-Operator direkt ins Syslog (Nix-Systeme) "schreiben" kann.

    Syslog funktioniert zeilenorientiert; d.h. man kann mit der Funktion syslog() immer nur eine ganze Zeile auf einmal ins Syslog schreiben. Wenn man Einträge mit Hilfe von verschiedenen Operatoren zusammensetzen will, wird bei meiner Implementation momentan jeder Operator-Aufruf einzeln ins Syslog geschrieben.

    Ich möchte nun mehrmals auf das Objekt "schreiben" können und erst mit std::endl die Ausgabe ins Syslog schreiben lassen.

    Ich gehe davon aus, dass sich ein solches Verhalten mit dem Erben von StreamBuffer schnell implementieren lässt. Schliesslich brauche ich einen Buffer, in welchem ich die Log-Nachricht zwischen-speichere, bis ich sie ins log schreibe.

    Meine Operatoren sehen so aus:

    Logger& operator <<(Logger& log, const char * msg) {
    	log.log(msg);
    	return log;
    }
    
    Logger& operator<<(Logger& log, const std::string msg) {
    	log.log(msg.c_str());
    	return log;
    }
    
    Logger& operator<<(Logger& log, const int msg) {
    	// convert integer and write to log
    	std::stringstream tmp;
    	tmp << msg;
    	log.log(tmp.str().c_str());
    	return log;
    }
    Logger& Logger::operator<<(StandardEndLine manip) {
    
    // JETZT Buffer ins Syslog schreiben und leeren. 
    // aufruf syslog(msg);
    
    	return *this;
    }
    

    So tief habe ich mich noch nie in den Standard Namespace hinein getraut. Aus der Doku unter http://www.cplusplus.com/reference/iostream/streambuf/ werde ich nicht ganz schlau.

    Welche Methoden muss ich überschreiben bzw. implementieren? Wie definiere ich den zu verwendenden Buffer?

    Vielen Dank für Tipps oder Verweise auf einfach Beispiele.


  • Mod

    Meinst du nicht, dass es WESENTLICH einfacher wäre, wenn du deiner Klasse einen voll funktionsfähigen Puffer (zum Beispiel std::stringstream) als Member spendierst?



  • SeppJ schrieb:

    Meinst du nicht, dass es WESENTLICH einfacher wäre, wenn du deiner Klasse einen voll funktionsfähigen Puffer (zum Beispiel std::stringstream) als Member spendierst?

    Ja, da hast du recht. Aber ich möchte doch dazu lernen und habe die Vermutung dass ich mit einer Spezialisierung weitere Vorteile ernte.

    Aber ja, ich habe das ganze jetzt mal mit einem strinbuffer realisiert.


  • Mod

    Wenn du es dir unbedingt schwerer machen musst, als es ist, findest du hier ein Beispiel zum Verwenden von streambuf:
    http://www.student.cs.uwaterloo.ca/~cs343/documents/libio/iostream_5.html

    Ich würde an deiner Stelle jedoch eher einen ostream nehmen und dessen streambuf dann auf syslog setzen. Oder die zuerst vorgeschlagene Methode mit dem stringstream, weil du dann einfacher das detailierte Verhalten kontrollieren kannst.



  • maus_on_c schrieb:

    Ich habe eine Klasse realisiert, auf welche man mit Hilfe des <<-Operator direkt ins Syslog (Nix-Systeme) "schreiben" kann.

    .. meines Erachtens suboptimal.

    maus_on_c schrieb:

    Syslog funktioniert zeilenorientiert; d.h. man kann mit der Funktion syslog() immer nur eine ganze Zeile auf einmal ins Syslog schreiben. Wenn man Einträge mit Hilfe von verschiedenen Operatoren zusammensetzen will, wird bei meiner Implementation momentan jeder Operator-Aufruf einzeln ins Syslog geschrieben.

    Ich möchte nun mehrmals auf das Objekt "schreiben" können und erst mit std::endl die Ausgabe ins Syslog schreiben lassen.

    Ich gehe davon aus, dass sich ein solches Verhalten mit dem Erben von StreamBuffer schnell implementieren lässt. Schliesslich brauche ich einen Buffer, in welchem ich die Log-Nachricht zwischen-speichere, bis ich sie ins log schreibe.

    std::streambuf ist die richtige Wahl. Den ostream kannst Du direkt weiter benutzen, egal ob mit einem eigenen ostream-Objekt oder cout bzw. clog

    maus_on_c schrieb:

    Welche Methoden muss ich überschreiben bzw. implementieren? Wie definiere ich den zu verwendenden Buffer?

    Das ist leider relativ wenig dokumentiert.
    Für die Ausgabe sind es die streambuf-Methoden sync und overflow. Hier mal ein Beispiel mit einem Buffer konstanter Größe.

    #include <iostream>
    #include <vector>
    
    void syslog( const char* txt ) // .. nur zu Demozwecken
    {
        std::cout << "syslog[" << txt << "]" << std::endl;
    }
    
    class LogBuf : public std::streambuf
    {
    public:
        enum { BUF_SIZE = 80 };
        LogBuf()
            : m_buf( BUF_SIZE+1 )
        {
            setp( &m_buf[0], &m_buf[0] + (m_buf.size()-1) );
        }
    protected:
        virtual int sync()
        {
            if( pptr() > pbase() )
                writeToDevice();
            return 0; // 0 := Ok
        }
    private:
        void writeToDevice()
        {
            *pptr() = 0; // End Of String
            syslog( pbase() );
            setp( &m_buf[0], &m_buf[0] + (m_buf.size()-1) );
        }
        // --  Member
        std::vector< char_type > m_buf;
    };
    
    int main()
    {
        using namespace std;
        LogBuf log;
        streambuf* oldSb = clog.rdbuf( &log ); // hier wird clog benutzt
        // alternativ: boost::io::ios_rdbuf_saver rbsvr( clog, &log ); // erfordert: #include <boost/io/ios_state.hpp>
    
        cout << "cout:" << endl;
        clog << "clog: " << endl;
        for( int n=0; n < 4; ++n  )
            clog << "  " << n << "/7=" << n/7. << endl;
    
        clog.rdbuf( oldSb );
        return 0;
    }
    

    In dem Beispiel wird der Text jedes mal weggeschrieben, bis ein flush erfolgt. Der endl-Manipulator löst einen solchen 'flush' aus. Das heißt aber auch, dass der Text z.B. Zeilenumbrüche enthalten kann - wie im Beispiel zu sehen ist.

    Falls der Buffer sich dynamisch vergrößern soll, so muss auch overflow überladen werden. overflow wird aufgerufen, wenn der Buffer voll ist.

    protected:
        virtual int_type overflow( int_type m = traits_type::eof() )
        {
            if( traits_type::eq_int_type( m, traits_type::eof() ) )
            {
                writeToDevice();
            }
            else
            {
                // Buffer overflow
                const std::size_t nChar = pptr()-pbase();
                m_buf.resize( 2*m_buf.size() ); // Buffer verdoppeln
                setp( &m_buf[0], &m_buf[0] + (m_buf.size()-1) );
                pbump( nChar );
                *pptr() = traits_type::to_char_type( m ); // überzähliges Zeichen anhängen
                pbump( 1 );
            }
            return traits_type::not_eof( m );
        }
    

    Gruß
    Werner



  • Ich staun ja jedes mal wieder, wenn Werner so was hinzaubert...
    Woher hast du das ganze Stream-Wissen?
    Doch wohl kaum einzeln aus Referenzen zusammen gesucht?!
    Gibt dazu irgendwo nen Tut oder nen Buch?

    bb



  • unskilled schrieb:

    Ich staun ja jedes mal wieder, wenn Werner so was hinzaubert...
    Woher hast du das ganze Stream-Wissen?
    Doch wohl kaum einzeln aus Referenzen zusammen gesucht?!
    Gibt dazu irgendwo nen Tut oder nen Buch?

    bb

    Wollte ich auch schon lange wissen, einfach nur 👍 beiträge.



  • Das Buch was ihr sucht: Standard C++ IOStreams and Locales von Klaus Kreft und Angelika Langer
    Sehr gute Einführung und Referenz in die gesamten Iostreams. Empfehlenswert zu lesen.



  • Bücherleser schrieb:

    Das Buch was ihr sucht: Standard C++ IOStreams and Locales von Klaus Kreft und Angelika Langer
    Sehr gute Einführung und Referenz in die gesamten Iostreams. Empfehlenswert zu lesen.

    vielen dank^^
    100$ für ein neues, klingt ja fast so, als ob es wahnsinnig dick und gut wäre^^
    naja, zum glück gibts gebrauchte ^^

    bb


Anmelden zum Antworten