operator<< definieren + Funktionalität an cout delegieren



  • Hiho

    ich habe eine Klasse geschrieben, für die ich den Ausgabeoperator überladen habe (und weitere Manipulatoren, im Beispiel manip ), so dass man sie wie cout verwenden kann.

    class MyCout
    {
      // ...
    };
    template<typename T>
    inline MyCout& operator<<(MyCout& c, T d)
    {
      std::cout << d;
      return (c);
    }
    
    inline MyCout& operator<<(MyCout& c, MyCout& (*pfn)(MyCout&))
    {
      pfn(c);
      return (c);
    }
    
    inline MyCout& manip(MyCout& c)
    {
      if(c.testSomething())
        c << "foobar";
      return (c);
    }
    

    Damit geht dann z.B. sowas:

    MyCout mc;
    mc << "Hallo! " << 42 << manip;
    

    Allerdings funktioniert ein

    MyCout mc;
    mc << endl;
    

    natürlich nicht mehr. Kennt jemand einen Weg, wie ich diese Funktionilät möglichst ohne großen Aufwand an "cout" delegieren kann (obwohl ich mit MyCout -Objekten arbeite), so dass also o.g. Beispiel dennoch funktioniert?

    Greetings! 😉



  • Du könntest "manip" in einen Funktor packen, dann kannst du auch gezielt dafür eine Überladung schreiben.

    PS: Was ist eigentlich der Sinn der Aktion?



  • CStoll schrieb:

    Du könntest "manip" in einen Funktor packen, dann kannst du auch gezielt dafür eine Überladung schreiben.

    Könntest Du (vllt. in Codeform) andeuten, wie Du das meinst? Kanns mir nicht ganz vorstellen...

    CStoll schrieb:

    PS: Was ist eigentlich der Sinn der Aktion?

    Farbige Ausgabe auf der Konsole. 😉



  • FooMan schrieb:

    CStoll schrieb:

    Du könntest "manip" in einen Funktor packen, dann kannst du auch gezielt dafür eine Überladung schreiben.

    Könntest Du (vllt. in Codeform) andeuten, wie Du das meinst? Kanns mir nicht ganz vorstellen...

    Nur grob angedeutet:

    class manip_t
    {
      ...
    };
    extern manip_t manip;
    
    MyCout operator<<(MyCout& stream, const manip_t& manip)
    {
      //mach was mit dem Stream
      return stream;
    }
    

    Da kannst du den Manipulator-Typ entweder als Dummy verwenden, um genau diese Operator-Überladung auszuwählen, oder du gibst der Klasse noch einen Konstruktor, um dir noch ein paar zusätzliche Daten mitzugeben (z.B. die zu verwendende Farbe).

    CStoll schrieb:

    PS: Was ist eigentlich der Sinn der Aktion?

    Farbige Ausgabe auf der Konsole. 😉

    Klingt interessant.
    Eventuell wäre es ja sogar möglich, dafür einen Manipulator zu schreiben, der direkt auf einen normalen Stream angewendet werden kann.



  • CStoll schrieb:

    FooMan schrieb:

    CStoll schrieb:

    Du könntest "manip" in einen Funktor packen, dann kannst du auch gezielt dafür eine Überladung schreiben.

    Könntest Du (vllt. in Codeform) andeuten, wie Du das meinst? Kanns mir nicht ganz vorstellen...

    Nur grob angedeutet:

    class manip_t
    {
      ...
    };
    extern manip_t manip;
    
    MyCout operator<<(MyCout& stream, const manip_t& manip)
    {
      //mach was mit dem Stream
      return stream;
    }
    

    Da kannst du den Manipulator-Typ entweder als Dummy verwenden, um genau diese Operator-Überladung auszuwählen, oder du gibst der Klasse noch einen Konstruktor, um dir noch ein paar zusätzliche Daten mitzugeben (z.B. die zu verwendende Farbe).

    Aber wie genau erlaubt mir das jetzt, meine Klasse ( MyCout ) für die Verwendung der Standardmanipulatoren (setfill,endl,hex,...) fitt zu machen? Die Brücke fehlt mir noch... 🙄



  • Dieser Operator wird nur für diesen Manipulator vom Compiler ausgewählt, alles andere landet in deinem obigen Template-Operator (und wird damit direkt weitergeleitet an die regulären Stream-Operatoren).



  • CStoll schrieb:

    Dieser Operator wird nur für diesen Manipulator vom Compiler ausgewählt, alles andere landet in deinem obigen Template-Operator (und wird damit direkt weitergeleitet an die regulären Stream-Operatoren).

    Da ich aber für jede Farbe einen solchen Manipulator (so wie manip) habe, müsste ich das dann auch für alle Manipulatoren tun, richtig? Das wäre dann halt sehr viel Schreibarbeit ... oder ich verstehe Dich noch nicht ... 🤡



  • Unterscheidet sich denn die prinzipielle Funktionsweise dahingehend, welche Farbe du verwenden willst? Wenn nicht, reicht eine Manipulator-Klasse, die du mit Parameter aufrufen kannst:

    class color
    {
      unsigned farbe;
    public:
      color(unsigned f) : farbe(f) {}
      unsigned getColor() const { return farbe; }
    };
    
    MyCout operator<<(MyCout& stream, color f)
    {
      ...
    }
    
    //optional, wenn dir die Direkt-Angabe zu aufwendig ist
    color red  (0x00FF0000);
    color green(0x0000FF00);
    color blue (0x000000FF);
    ...
    


  • CStoll schrieb:

    Unterscheidet sich denn die prinzipielle Funktionsweise dahingehend, welche Farbe du verwenden willst? [...]

    Nein, stimmt. Wenn ich das allerdings so implementiere und es wie folgt teste:

    MyCout mc;
    mc << color("\033[22;31m") << "Hallo" << endl;
    

    ...meldet der Compiler:

    Main.cpp:60:46: Fehler: no match for »operator<<« in »gfxout::operator<< [with T = const char*]((* & gfxout::operator<<((* & mc), gfxout::color((* & std::basic_string<char>(((const char*)"\033[22;31m"), (*(const std::allocator<char>*)(& std::allocator<char>()))))))), ((const char*)"Hallo")) << std::endl«
    

    ...ohne endl funktioniert es ( gfxout ist ein namespace). Meine Ausgabe-Operatoren sind nur die beiden:

    template<typename T>
    inline MyCout& operator<<(MyCout& c, T d)
    {
      std::cout << d;
      return (c);
    }
    
    inline MyCout& operator<<(MyCout& s, color f)
    {
    	return (s << f.getColor());
    }
    


  • Mal eine Randfrage: Brauchst du dazu überhaupt eine eigene Dummy-Klasse MyCout? Wenn nicht, würde ich nur die Color-Klasse definieren und ihren Stream-Operator passend überladen.



  • CStoll schrieb:

    Mal eine Randfrage: Brauchst du dazu überhaupt eine eigene Dummy-Klasse MyCout? Wenn nicht, würde ich nur die Color-Klasse definieren und ihren Stream-Operator passend überladen.

    Hatte ich auch schon überlegt, aber leider ja: Ich möchste halt mc << red << "Hallo" << endl; schreiben können vorher einmal, bei Erzeugung von mc sagen, "Farben an" oder "Farben aus". Wenn Farben ausgestellt sind, wird das "red" einfach ignoriert. Man könnte das mit globalen Variablen lösen, aber das finde ich unschön...



  • Da würde ich einen idealen Einsatz für die Zusatzflags der Streams sehen:

    static const int colorflag_index = std::ios_base::xalloc();
    
    std::ostream& color_on(std::ostream& stream)
    {
      stream.iword(colorflag_index) = true;
      return stream;
    }
    
    std::ostream& color_off(std::ostream& stream)
    {
      stream.iword(colorflag_index) = false;
      return stream;
    }
    
    std:.ostream& operator<<(std::ostream& stream, color c)
    {
      if(stream.iword(colorflag_index))
        stream<<c.getCode();
      return stream;
    }
    

    Damit kannst du sogar für jeden Stream einzeln festlegen, ob er Farben verwenden soll - wenn du nichts angibst, sind sie per Default abgeschaltet. Und mit einem cout<<color_on; schaltest du die Farb-Darstellung ein, wenn du sie benötigst.



  • Perfekt, vielen-vielen Dank! 👍


Anmelden zum Antworten