std::cout umleiten



  • Moin

    Ich schlage mich jetzt schon seid längerem mit einem Problem rum und hoffe einer von Euch kann mit helfen.
    Ich würde gerne den stream von std::cout für mich nutzen um ihn in meine „ingame-console“ umzuleiten. Eine abgeleitete streambuf Klasse habe ich mir bereits geschrieben und durch dir Überlagerung von „xsputn“ bekomme ich auch einige Ergebnisse. Leider nimmt mein stream
    aber nur Daten vom Typ char. Variablen vom Typ integer, float, std::string u.s.w. nimmt er nicht. Hat jemand eine Idee wie das lösen kann?

    {
      C_MyStream my_stream;
    
      std::cout.rdbuf(&my_stream);
    
      /// geht z.B. :
      std::cout<< "BlaBlaBla\n";
      std::cout<< "BlaBlaBla "<<"blub"<< "2";
    
      /// geht z.B. nicht :
      float   f=0.1f;
      double  d=0.2f;
    
      std::cout<<"test" << 2 << std::endl; // wird nur bis "test" ausgegeben.
      std::cout<< f << d <<std::endl;
    
     }
    


  • xsputn?!
    Ich kenne nur overflow und sync.
    Hast du vielleicht irgendwelche Fehlermeldungen? Auch praktisch könnte es sein, deine Streambufferklasse zu sehen...



  • Servuz,

    ich hänge mich hier mal mit rein, da die Überschrift passt und man ja keinen neuen Thread aufmachen muss.

    Ich würde auch gern std::cout umleiten, da ich eine CGI Anwendung in C++ geschrieben hab die die Standardausgabe an den Webserver weitergibt. Dieser leitet dann die Daten an den Browser weiter.

    Nun arbeite ich mit einem Framework das auch zwischendurch Daten über std::cout ausgibt. Darüber hab ich keine Kontrolle 😡, was mir natürlich die Daten für den Webserver zerstört.

    Ich würde nun gerne std::cout auf ein anderes Ausgabemedium als die Konsole bzw. stdout (<- ist es das) umleiten und mir evtl. einen eigenen Stream bauen den ich dann auf die Konsole geben kann, damit der Webserver seine Daten bekommt...

    Ich denke mal das passt auch zum Thema std::cout umleiten. Falls nicht mach ich auch gern nen eigenen Thread auf... 👍

    Gruß und hallo Community
    Spanky



  • cout ^= Console-Out. Mach lieber einen neuen Stream. Dafür gibt es zwei Wege:
    1. "zu Fuß"
    2. Mit Boost.Iostreams



  • cout ^= Character-Out



  • cout ^= Console-Out. Mach lieber einen neuen Stream. Dafür gibt es zwei Wege:
    1. "zu Fuß"
    2. Mit Boost.Iostreams

    Danke für die Tips, werde mich der "zu Fuß" Lektüre widmen und rückmelden. Eine Bibliothek einzubinden steht wohl nicht zur Debatte, da der Code klein gehalten werden soll.

    Bleibt aber immernoch das Problem wie ich die vom Framework kommenden Ausgaben abfange, oder? 😕

    Gruß

    EDIT: Also ich bin jetzt mit dem "zu Fuß" Artikel durch...versteh aber noch nicht ganz wie ich mir das zu nutzen machen soll. Ich habe dann zwar einen eigenen Stream, der landet auch auf einer Konsole. Aber die erzeuge ich mir ja....hm....



  • Auch ich bedanke mich.
    Werde mahl etwas experimentieren.

    Gruß und Dank Polignome



  • Sorry, hatte ich überlesen. Du kannst den Streambuffer von cout mit rdbuf ändern:

    streambuf* consolebuffer = std::cout.rdbuf ();
    std::cout.rdbuf (&mybuffer);
    

    rdbuf gibt ohne Argumente den alten Streambuffer zurück, mit einem streambuf*-Zeiger setzt es zusätzlich diesen Streambuffer ein.



  • // redirecting cout's output
    #include <iostream>
    #include <fstream>
    using namespace std;
    
    int main () {
      streambuf *psbuf;
      ofstream filestr;
      filestr.open ("test.txt");
    
      psbuf = filestr.rdbuf();
      cout.rdbuf(psbuf);
    
      cout << "This is written to the file";
    
      filestr.close();
    
      return 0;
    }
    

    😕



  • streambuf* consolebuffer = std::cout.rdbuf ();
    std::cout.rdbuf (&mybuffer);
    rdbuf gibt ohne Argumente den alten Streambuffer zurück, mit einem streambuf*-Zeiger setzt es zusätzlich diesen Streambuffer ein.

    Also wenn ich das jetzt richtig verstehe, erste Zeile leitet cout um in consolebuffer. Und dann setze ich danach cout auf meinen Stream? 😕

    EDIT:
    Von was für einem Typ muss denn mybuffer sein??? streambuff??? Da bekomm ich die Fehlermeldung: "error: within this context"....

    EDIT2:

    .filmor schrieb:

    Lies dir die beiden verlinkten Seiten durch, bevor du nochmal postest.

    Ist ja gut... 😡 bin ich doch schon dabei... mir fehlt halt noch bisschen das Verständnis dafür 👎



  • Lies dir die beiden verlinkten Seiten durch, bevor du nochmal postest.



  • Hier ist mein alter Ansatz:

    template <class charT, class traits = std::char_traits<charT> >
    class CDG_CoutRerouteStr : public std::basic_streambuf<charT, traits>
    {
    	public:
    		CDG_CoutRerouteStr() {};
    	protected:
    		std::streamsize xsputn(const charT * pChar, std::streamsize n)
    		{
    			std::cerr <<pChar; // Testausgabe 	
    
     		      return static_cast<std::streamsize>(l.length()); 
    		}
    
    private:
    	CDG_CoutRerouteStr(const CDG_CoutRerouteStr&);
    	CDG_CoutRerouteStr & operator=(const CDG_CoutRerouteStr&);
    
    };
    
    void main(void) 
    {
    
      CDG_CoutRerouteStr<char> mybuffer;
    
      // sichern des alten stream
      streambuf* old_buffer = std::cout.rdbuf ();
    
      // setzen des neuen streams
      std::cout.rdbuf (&mybuffer);
    
      std::cout <<"blablabla"<<std::endl;
    
      // setzen alten streams
      std::cout.rdbuf (old_buffer);
    
    }
    


  • polignome schrieb:

    Eine abgeleitete streambuf Klasse habe ich mir bereits geschrieben und durch dir Überlagerung von „xsputn“ bekomme ich auch einige Ergebnisse.

    Wenn ein Ausgabe-Streambuf überladen wird, der mehr als eine festgesetzte Menge von Zeichen aufnehmen soll - und das ist bei cout faktisch der Fall - so muss auf jeden Fall overflow überladen werden. xsputn ruft im nicht überladenen Fall auch nur overflow auf.

    Im Prinzip sieht das etwa so aus:

    void hauWech( char c );
    

    sei Deine Funktion die zeichenweise den Output aufnehmen soll.

    class MyBuf : public std::basic_streambuf< char >
    {
    protected:
        virtual int_type overflow( int_type c )
        {
            if( !traits_type::eq_int_type( c, traits_type::eof() ) )
                hauWech( traits_type::to_char_type( c ) );
            return traits_type::not_eof( c );
        }
    };
    

    Das Überladen von xsputn lohnt sich nur wenn Du große Strings hast, und Dein konkreter Buffer auch große Mengen von Daten 'am Stück' aufnehmen kann.

    Gruß
    Werner



  • Danke Werner.
    Das ist es.
    Hatte auch eine andere Lösung gefunden aber deine ist eleganter.

    Gruß Polignome



  • Ich nochmal,

    Also ich hab es jetzt soweit hinbekommen. Ich hab lediglich wie Werner beschrieben hat eine Klasse abgeleitet und dann einen eigenen Stream benutzt.

    myStreambuf.h

    #include <iostream>
    
    class myStreambuf : public std::streambuf
    {
    public:
    	myStreambuf();
    	virtual ~myStreambuf();
    
    protected:
    	virtual int overflow(int_type c);
    
    private:
    	int hauWech(char c);
    
    };
    

    myStreambuf.cpp

    #include "myStreambuf.h"
    
    myStreambuf::myStreambuf()
    {
    }
    
    myStreambuf::~myStreambuf()
    {
    }
    
    int myStreambuf::overflow(int_type c)
    {
    	if(!traits_type::eq_int_type(c, traits_type::eof()))
    		hauWech( traits_type::to_char_type(c));
    	return traits_type::not_eof(c);	
    }
    
    int myStreambuf::hauWech(char c)
    {
    		return 0;
    }
    

    und in der main.cpp

    myStreambuf coutNirvanaBuf;
    
    //save the old stream
    streambuf* old_buffer = std::cout.rdbuf();
    //set to new stream
    cout.rdbuf(&coutNirvanaBuf);
    
    ostream newcout(old_buffer);
    

    Ich kann dann mein newcout auch einwandfrei benutzen. Ich hab nur ein Problem. Unter Windows bekomme ich bei der Beendigung des Programms eine "Access Violation"

    Muss ich den Stream am Ende wieder zurückbiegen bevor ich das Programm beende?

    Auf die Gefahr hin das ihr mich erschlagt, hab ich dann doch gedacht ich frag nochmal! 😃

    Gruß
    Spanky



  • Spankmaster79 schrieb:

    Ich kann dann mein newcout auch einwandfrei benutzen. Ich hab nur ein Problem. Unter Windows bekomme ich bei der Beendigung des Programms eine "Access Violation"

    Muss ich den Stream am Ende wieder zurückbiegen bevor ich das Programm beende?
    Spanky

    Hallo Spanky,

    es gibt ein paar allgemein gültige Regeln. Eine davon ist: "Hinterlasse jeden Platz, den Du besuchst, so sauber wie Du ihn vorgefunden hast." Das gilt für Picknick-Plätze, Toiletten und auch std::cout 😉

    Ja, unter Windows musst Du den alten Streambuf wieder zurücksetzen. Also:

    myStreambuf coutNirvanaBuf;
    
    // switch the streambuf
    streambuf* old_buffer = std::cout.rdbuf( &coutNirvanaBuf );
    
    ostream newcout(old_buffer);
    
    // ... cout geht in's Nirwana; newcout geht dahin, wo vorher cout war
    
    // -- vor Programm-Ende
    std::cout.rdbuf( old_buffer  );
    

    Für dieses Um- und Zurückschalten solltest Du die RAII-Methode benutzen. Das sieht ungefähr so aus

    #include <ios>
    class IosSwitch  
    {
    public:
        IosSwitch( std::basic_ios< char >& ios, std::streambuf* sb )
            : m_ios( &ios )
            , m_sbMerk( ios.rdbuf( sb ) )
        {}
    
        ~IosSwitch()
        {
            if( m_ios )
            {
                m_ios->rdbuf( m_sbMerk );
            }
        }
        std::streambuf* rdbuf() const { return m_sbMerk; }
    
    private:
        std::basic_ios< char >* m_ios;
        std::streambuf*         m_sbMerk;
    
        // ---  nicht kopierbar
        IosSwitch( const IosSwitch& );
        IosSwitch& operator=( const IosSwitch& );
    };
    

    Die Anwendung ist dann zum Beispiel

    int main()
    {
        myStreambuf coutNirvanaBuf;
        IosSwitch coutSwitch( std::cout, &coutNirvanaBuf );
        // ... cout geht in's Nirwana
    
        ostream newcout( coutSwitch.rdbuf() );
        // ... newcout geht dahin, wo vorher cout war
    
        return 0;
    } // mit Verlasen des Scopes wird zurückgeschaltet
    

    So kann man's nicht vergessen, und auch nach einer Exception wird std::cout wieder aufgeräumt.

    Gruß
    Werner



  • Soweit ich weiß kann man einen stream auch mit
    redirect(stream, Ziel);
    umleiten. Ich habe mir diese Möglichkeit aber noch nicht genauer angesehen. Sieht jedoch sehr einfach aus.

    Ich habe aber ebenfalls eine ähnliche Frage. Ist es möglich mit einem Stream auf zwei Ausgänge gleichzeitig zu schreiben?

    Ich habe ein Programm, dass exakt den selben Inhalt, den es mit cout auf dem Bildschirm ausgibt, auch in eine Datei schreibt. Es wäre also das einfachste, wenn cout gleichzeitig auf den Bildschirm, als auch in die Datei schreibt.

    Ich suche jetzt schon eine ganze Weile bei google, finde aber nichts wirklich hilfreiches. Hat jemand hier eine Lösungsidee?



  • Soweit ich weiß kann man einen stream auch mit
    redirect(stream, Ziel);

    Werd ich auch mal nach schauen...danke.

    @Werner
    Danke für den Tip mit RAII. Werd es noch so versuchen umzusetzen. Anders (nur mit zurücksetzen) geht es ja auch.

    Noch eine Frage zu RAII. Gehört es auch zum Konzept den coutSwitch als Singleton zu definieren (das macht doch der Privat gesetzte Kopierkonstruktor, oder?) ? Und wieso brauch ich einen Zuweisungsoperator, den ich dann nicht benutze?

    Gruß
    Spanky



  • gebirgsbaerbel schrieb:

    Soweit ich weiß kann man einen stream auch mit
    redirect(stream, Ziel);
    umleiten.

    'redirect' kenne ich nicht und gehört auch nicht zum C++-Standard. 😕

    gebirgsbaerbel schrieb:

    Ist es möglich mit einem Stream auf zwei Ausgänge gleichzeitig zu schreiben?

    Ich habe ein Programm, dass exakt den selben Inhalt, den es mit cout auf dem Bildschirm ausgibt, auch in eine Datei schreibt. Es wäre also das einfachste, wenn cout gleichzeitig auf den Bildschirm, als auch in die Datei schreibt.

    Das ist kein Problem. Du änderst einfach den oben schon beschriebenen MyBuf so ab, dass er in zwei weitere streambufs schreibt. Alles andere geht wie gehabt.

    #include <iostream>
    #include <fstream>
    #include <streambuf>
    
    class Spreadbuf : public std::basic_streambuf< char >
    {
    public:
        Spreadbuf( std::basic_streambuf< char >* sb1, std::basic_streambuf< char >* sb2 )
            : m_sb1( sb1 )
            , m_sb2( sb2 )
        {}
    
    protected:
        virtual int_type overflow( int_type c = traits_type::eof() )
        {
            if( !traits_type::eq_int_type( c, traits_type::eof() ) )
            {
                const char x = traits_type::to_char_type( c );
                if( traits_type::eq_int_type( m_sb1->sputc( x ), traits_type::eof() )
                        || traits_type::eq_int_type( m_sb2->sputc( x ), traits_type::eof() ) )
                    return traits_type::eof();
            }
            return traits_type::not_eof( c );
        }
    
    private:
        std::basic_streambuf< char >* m_sb1;
        std::basic_streambuf< char >* m_sb2;
    };
    
    int main()
    {
        using namespace std;
    
        ofstream file( "coutlog.txt" );
        Spreadbuf spready( cout.rdbuf(), file.rdbuf() );
        {
            IosSwitch coutswitch( cout, &spready );
            cout << "Jetzt geht alles auf cout auch in die Datei 'coutlog.txt' " << endl;
            cout << "Jede Zahl: " << 1./7 << endl;
            cout << " .. und jedes Zeichen: " << '#' << endl;
        }
        cout << "und jetzt ist alles wie vorher";
        file << "--- Ende --- " << endl;
        return 0;
    }
    

    'IosSwitch' ist so wie in meinem vorhergehenden Posting.

    Gruß
    Werner



  • Spankmaster79 schrieb:

    Danke für den Tip mit RAII. Werd es noch so versuchen umzusetzen. Anders (nur mit zurücksetzen) geht es ja auch.

    Hallo Spanky,
    .. mit RAII ist besser als ohne; glaub's mir. Und macht auch irgendwann irgendwie süchtig 😉

    Spankmaster79 schrieb:

    Noch eine Frage zu RAII. Gehört es auch zum Konzept den coutSwitch als Singleton zu definieren (das macht doch der Privat gesetzte Kopierkonstruktor, oder?) ? Und wieso brauch ich einen Zuweisungsoperator, den ich dann nicht benutze?

    Richtig ist, dass man bei einem Singleton (auch) den Kopierkonstruktor privat macht, um ein Kopieren zu verhindern. Aber ein privater Kopierkonstruktor macht noch kein Singleton.
    Ein Singleton ist eine Klasse, von der es nur ein Objekt gibt (bzw. geben soll). Bei eine Klasse mit privaten Kopierkonstruktor können Objekte nicht durch eine Kopie wohl aber durch einen weiteren Konstruktor-Aufruf entstehen. Soviele Du willst.
    Überleg Dir einfach mal, was passiert, wenn Du von IosSwitch eine Kopie anlegst und die Kopie vor dem Original löscht; evt. unbewußt.
    Objekte die man für RAII benutzt, sollten grundsätzlic nicht kopierbar sein, weil sonst Resourcen mehrfach freigegeben werden. Oder die Kopie- und Zuweisungsoperatoren müssen das berücksichtigen; prominentes Beispiel ist boost::shared_ptr<>. In diesem Zusammenhang interessant ist auch die Regel der drei.

    Gruß
    Werner


Anmelden zum Antworten