wie "endl" definieren bei eigenem Stream



  • Hallo,

    ich habe eine eigene kleine Klasse von ostringstream abgeleitet.

    mache ich ein

    mystream << "Text";
    

    dann landet der String im internen Puffer rdbuf. Wenn durch ein

    mystream << endl;
    

    ein flush ausgelöst wird, soll der Inhalt durch meine Klasse weiterbehandelt werden.

    Irgendwie gelingt es mir nicht, ein eigenes flush() zu basteln. Der Compiler nimmt sich das Standard flush(), das ich aber hier nicht will.

    Irgendwas mach ich falsch

    class jhStream : public std::ostringstream
    {
    public:
      virtual jhStream &flush();
    };
    
    jhStream& jhStream::flush (void)
    {  putstr( str()); // wohin schaufeln!
       str("");
       return (*this);
    }
    

    Meine Idee war, dass mit

    jhStream mystream;
    mystream << "hello world!" << endl;
    

    mein flush() aufgerufen wird. Tutet aber nicht 😞

    MQ



  • MasterQ schrieb:

    ich habe eine eigene kleine Klasse von ostringstream abgeleitet.

    Eine Klasse die vermutlich wie nahezu alle Klassen der Standardbibliothek nicht als Basisklasse taugt. Ich behaupte nun einfach das es keine virtuellen Funktionen in der ostringstream gibt...



  • Wie der eigentliche Titel vermuten lässt, ist meine eigentliche Absicht, ein eigenes endl zu basteln.

    jhStream& endl (jhStream& out);
    
    jhStream& endl (jhStream& out)
    {  out << '\n';
       out.flush(); //mein flush() !?
    
       return out;
    }
    

    und

    jhStream mystream;
    mystream << endl;
    

    zeigen, dass mein endl nicht verwendet wird.

    Nenne ich endl um in myendl oder so, dann wird im Debugger deutlich, dass er meine Funktion nicht als passend akzeptiert und dann nichts macht.

    Wo kneift es??

    gruß

    MQ



  • asc schrieb:

    Eine Klasse die vermutlich wie nahezu alle Klassen der Standardbibliothek nicht als Basisklasse taugt. Ich behaupte nun einfach das es keine virtuellen Funktionen in der ostringstream gibt...

    nun gut (oder schlecht), wenn dem so sei!

    Aber ich könnte das Problem der virtuellen Funktion ja umgehen, wenn ich mein eigenes endl bastele, das dann anstatt des Standard-endl verwendet werden würde. Wie in meiner ersten Reaktion aber deutlich wurde, kann ich ein wieauchimmerendl basteln und dann ein

    jhstream << wieauchimmerendl
    

    aufrufen.

    Mein wieauchimmerendl wird nicht angenommen. endl ist kein Bestandteil der Klasse und muss daher nicht virtual sein. Trotzdem geht es nicht.

    MQ



  • MasterQ schrieb:

    Wie der eigentliche Titel vermuten lässt, ist meine eigentliche Absicht, ein eigenes endl zu basteln.

    Nein. Deine eigentliche Absicht war doch, einen eigenen Stream zu basteln und dein aktuelles Problem liegt darin, dass das mit dem endl nicht funktioniert.

    Das liegt daran, dass der operator<< für ostream-manipulatoren überladen ist, für deine stream-manipulatoren aber nicht.

    Ich würd das so versuchen (ungetestet):

    typedef jhStream& (manipulator)(jhStream&);
    
    jhStream& operator<< (jhStream& stream, manipulator manip)
    {
      return manip(stream);
    }
    


  • pumuckl schrieb:

    Nein. Deine eigentliche Absicht war doch, einen eigenen Stream zu basteln und dein aktuelles Problem liegt darin, dass das mit dem endl nicht funktioniert.

    Meine Absicht war, eine alte Konsolenanwendung für eine GUI aufzumotzen und alle alten cout-Aufrufe auf möglichst einfache Weise zu erweitern, dass die Ausgabe auch in meinem Fenster landet. Das funktioniert soweit auch. Nur das Abfangen des flush, der Trigger um den Inhalt ans Fenster zu übergeben, klappt nicht.

    Das liegt daran, dass der operator<< für ostream-manipulatoren überladen ist, für deine stream-manipulatoren aber nicht.

    Das hatte ich auch mal probiert. Ich bin nicht der C++-Experte und habe mir angekuckt, wie es VS2008 macht und da kam sowas bei rum.

    jhStream& operator<< (jhStream &out, const void *val){return out};
    

    wenn ich jetzt meinen Stream aufrufe, z.B.

    mystream << "test" << asciioutput() << endl;
    

    dann explodieren die Fehlermeldungen.

    1>.\xxxx : error C2666: 'operator <<' : 6 overloads have similar conversions

    Und da hängt es jetzt. Dass der operator<< für meinen Stream nicht überladen ist, ist richtig.

    Nur, wie kriege ich diese 6 Ambivalenzen weg wenn er überladen ist. Wie krieg es hin, dass die Auswahl eindeutig ist?

    MQ



  • MasterQ schrieb:

    Und da hängt es jetzt. Dass der operator<< für meinen Stream nicht überladen ist, ist richtig.

    Darum gehts nicht. Dein Stream ist ein ostream (wegen vererbung), damit funktionieren die "normalen" operator<< auch dort. Das Problem ist, dass die normalen Stream-Manipulatoren Funktionen sind, die einen ostream nehmen und einen ostream zurückgeben. Für solche Funktionen ist op<< auch überladen, das heißt die normalen ostream-manipulatoren kannst du auch bei deinem Stream benutzen. aber dein endl ist eben eine andere Funktion, für die es noch keine Operatorüberladung gibt.

    Was du jetzt gemacht hast ist, eine Überladung für const void* zu machen, das ist aber auch nicht der Typ deines endl. Der typ deines endl ist
    jhStream&(*)(jhStream&) , die Signatur des operators müsste also etwa wie folgt aussehn:

    class jhStream 
    {
    public: jhStream& operator<<(jhStream& (manip*)(jhSTream&));
    };
    

    Damit geht dann zumindest folgendes (mal angenommen dein endl liegt im namespace jh):

    jhStream jout;
    jout << jh::endl;
    

    Was NICHT gehen wird ist, vor dem endl anderes an den Stream zu schicken. Der Grund: alle "normalen" op<< haben als rückgabewert einen std::ostream, und der weiß wieder nichts mit deinen Manipulatorfunktionen anzufangen. Was ich daher machen würde ist, folgenden Manipulator zu schreiben:

    namespace jh  //oder wie auch immer dein namespace heißt
    {
      std::ostream& endl(std::ostream& os) //ganz normaler ostream-Manipulator!
      {
        try 
        {
          jhStream& jhs = dynamic_cast<jhStream&>(os); //wenn os kein jhStream ist fliegt ne exception
          jhs << '\n';
          jhs.flush();  //dank cast können wir jhStream::flush aufrufen!
        }
        catch(...)
        {
          os << std::endl; //bei exception: war kein jhStream, also einfach std::endl
        }
        return os;
      }
    } //end namespace jh
    

    Mit dem Ding brauchst du keinen eigenen op<< zu definieren, das funktioniert dann sogar mit std::ostreams



  • n'abend,

    pumuckl schrieb:

    Was NICHT gehen wird ist, vor dem endl anderes an den Stream zu schicken.

    das habe ich befürchtet. Und damit habe ich mein Problem. Meine Absicht war es, den alten Code so wenig wie möglich anzufassen. Wenn ich alle endls in eine eigene Anweisung packen muss, dann muss ich erheblich in den alten Code rein.

    hmm, muss mal drüber nachdenken, wie ich dann weiter vorgehe.

    Danke

    MQ



  • Du solltest nicht die Streamklasse, sondern die Streambufferklasse modifizieren (die hat nämlich auch virtuelle Funktionen, so dass dein flush auch aufgerufen wird). Dann musst du nur in main für std::cout und Konsorten den Puffer ersetzen und du musst nichtmal am übrigen Code was verändern.
    http://spec.winprog.org/streams/ geht recht tiefgehend darauf ein, aber du kannst ja den Anfang überspringen und dir nur den zugehörigen Code ansehen 😉



  • Hallo,

    .filmor schrieb:

    Du solltest nicht die Streamklasse, sondern die Streambufferklasse modifizieren ... http://spec.winprog.org/streams/

    erstmal vielen Dank für diesen Tipp.

    Wie das so ist mit den Beispielen, bilden diese nicht wirklich 100%ig das eigene Problem ab. Aber ich denke, ich bin auf einem guten Weg.

    Ich hänge im Moment daran, dass wohl kein internen Buffer zur Verfügung steht, wenn man es so macht, wie ich. Im folgenden mein Code, der sich an das Beispiel im Link anlehnt:

    Ich leite meinen Streambuffer von stringbuf ab, denn ich will strings behandeln.

    class jhstreambuf : public std::stringbuf
    {  
       protected:
         typedef std::char_traits<char> _Tr;
         virtual int overflow( int = _Tr::eof());
       private:
         jhx *pMainWindow;
         int color;
         char internbuf[ 1024];
       public:
         jhstreambuf();
         ~jhstreambuf();
         void set( jhx *pMainWindow, int color);
    };
    
    jhstreambuf _coutbuf;
    jhstreambuf _cerrbuf;
    
    jhstreambuf::jhstreambuf()
    : std::stringbuf( ios_base::out)
    {   pubsetbuf( internbuf, 1024);
    }
    
    jhstreambuf::~jhstreambuf()
    {
    }
    
    void jhstreambuf::set( jhx *iMainWindow, int icolor)
    {  pMainWindow = iMainWindow;
       color = icolor;
    }
    
    int jhstreambuf::overflow( int i)
    {  if( i == _Tr::eof()) return _Tr::not_eof( i);
       stringbuf::overflow( i);
       pMainWindow->PutStr( str(), color);
       return i;
    }
    

    in meinem Programm dann:

    _coutbuf.set( this, black);
          cout.rdbuf( &_coutbuf);
          cout << "123" << endl;
    

    Im Prinzip funktioniert das. Ausgegeben wird aber nur das erste zeichen, also die "1". Dann ist Schluss.

    Geh' ich mit dem Debugger durch, dann sehe ich, dass der Constructor von stringbuf die Hinweise enthält, dass dies die Variante "ohne Buffer" wäre. Den Constructor gibt es auch mit einen string als Initialisierer, doch wenn ich diesen verwende, dann bessert sich nix.

    Auch wenn ich mit pubsetbuf einen Buffer setze, keine Änderung! ??

    Wo hakt es??

    Gruß

    MQ



  • redefiniere mal 'int sync()' im streambuf.
    wird von pubsync oder so aufgerufen.

    sync() soll bei aufruf 'overflow()' aufrufen.

    class jhstreambuf : public std::stringbuf
    {  
       protected:
         typedef std::char_traits<char> _Tr;
         virtual int overflow( int = _Tr::eof());
         virtual int sync(void) { return overflow(); } // <--------------
       private:
         jhx *pMainWindow;
         int color;
         char internbuf[ 1024];
       public:
         jhstreambuf();
         ~jhstreambuf();
         void set( jhx *pMainWindow, int color);
    };
    

    Meep Meep


Anmelden zum Antworten