stream manipulator fuer colored console output



  • Hallo zusammen,

    um die wichtigesten Ereignise in meinen Programmen hervorzuheben wollte ich einen stream manipulator erstellen der mir die aktuelle font/background color setzt.

    Jetzt hab ich dazu Fragen:

    ich hab mir das ca so vergestellt:

    std::cout << "[timestamp] - " << color(RED) << "error" << color(DEFAULT) << "[text]" << "\n";
    //oder
    std::cout << "connected to: " << color(GREEN) << "[ip]" << "\n";
    std::cout << "normal text\n"; // dieser text soll jetzt wieder in der default farbe sein.
    //oder fuer font/background color
    std::cout << color(BLACK, WHITE) << "text with different background/font" << "\n"; // hier das gleiche nach einem \n soll wieder auf default geschaltet werden
    

    Seh ich des richtig, dass ich jetzt einen manipulator functor (wegen den 2 parametern glaub ich, muss sagen so 100%ig kenn ich mich mit dennen ned aus) "color" erstelle und ich aber zusaetzlich eine class schreiben muss die von streambuf erbt (so wies Werner Salomon mit dem XOR gemacht hat) um zu erkennen wann ein "\n" oder ein beliebiges token kommt und ich die color wieder auf default setzen kann?

    Gibt zu den Streams / Manipulator im Internet eine gute Referenz.
    Hab den Buch vorschlag (glaub auch von Werner Salomon) gesehen aber ein ganzes Buch fuer das ist mir eigenlich zu viel.

    Gruessle stuxn



  • Ja, wenn du jedes Zeichen das Ausgegeben wird, prüfen willst, musst du einen eigenen Stream Buffer schreiben.

    Hier mal meine Version mit der WinAPI 🤡 🤡 :

    #include <iostream>
    #include <windows.h>
    
    enum COLOR
    {
            black,
            blue,
            green,
            cyan,
            red,
            magenta,
            brown,
            light_grey,
            dark_grey,
            light_blue,
            light_green,
            light_cyan,
            light_red,
            light_magenta,
            yellow,
            white,
            default_text_color = white,
            default_background_color = black
    };
    
    struct color_manipulator
    {
            color_manipulator(int c):
                    color_code(c) {}
    
            int color_code;
    };
    
    color_manipulator color(COLOR text_color, COLOR background_color = COLOR::black)
    {
            return text_color + 16 * background_color;
    }
    
    std::ostream& operator<<(std::ostream& os, color_manipulator c)
    {
            SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), c.color_code);
            return os;
    }
    
    class color_resetter : public std::streambuf
    {
            std::streambuf* mTarget;
    
    protected:
    
            virtual int_type overflow(traits_type::int_type m = traits_type::eof())
            {
                    if(traits_type::eq(m, '\n')) //Muss hier vorher noch ein Vergleich zu eof mit eq_int_type rein?
                            std::cout << color(default_text_color, default_background_color);
    
                    return mTarget->sputc(m);
            }
    
    public:
    
            color_resetter(std::streambuf *target):
                    mTarget(target) {}
    };
    
    int main()
    {
            color_resetter resetter(std::cout.rdbuf());
            std::streambuf* old_streambuf = std::cout.rdbuf(&resetter);
    
            /// *** DEIN BEISPIEL *** 
            std::cout << "[timestamp] - " << color(red) << "error" << color(white) << "[text]" << "\n";
            //oder
            std::cout << "connected to: " << color(green) << "[ip]" << "\n";
            std::cout << "normal text\n";
    
            std::cout << color(black, white) << "text with different background/font" << "\n";
    
            std::cout.rdbuf(old_streambuf);
    }
    

    Aber da müsste nochmal Werner oder so drüberschaun', bin sicher ich hab' ein Paar Fehler gemacht.



  • Sone schrieb:

    Ja, wenn du jedes Zeichen das Ausgegeben wird, prüfen willst, musst du einen eigenen Stream Buffer schreiben.

    Hier mal meine Version mit der WinAPI 🤡 🤡 :

    ...
    

    Aber da müsste nochmal Werner oder so drüberschaun', bin sicher ich hab' ein Paar Fehler gemacht.

    Perfekt, genau das was ich auch machen wollt. Ich hab jetzt nur noch die color_resetter Klasse angepasst. Imho macht das mehr Sinn:

    class color_resetter : public std::streambuf
    {        
    public:
    	static const char RESET_TOKEN = '\n';
    
    	color_resetter(std::ostream& stream):
    		m_target( stream.rdbuf() ), m_stream( stream)
    	{
    		stream.rdbuf( this );
    	}	
    	~color_resetter() {
    		m_stream.rdbuf( m_target );
    	}
    protected:
    
        virtual int_type overflow(traits_type::int_type m = traits_type::eof())
        {
                if(traits_type::eq(m, RESET_TOKEN)) //Muss hier vorher noch ein Vergleich zu eof mit eq_int_type rein?
                        std::cout << color(default_text_color, default_background_color);
    
                return m_target->sputc(m);
        }
    
    private:
    	std::ostream& m_stream;
    	std::streambuf* m_target;
    };
    

    Somit kann man statt:

    int main()
    {
            color_resetter resetter(std::cout.rdbuf());
            std::streambuf* old_streambuf = std::cout.rdbuf(&resetter);
    
            /** ausgabe **/
    
            std::cout.rdbuf(old_streambuf);
    }
    

    einfach:

    int main()
    {
            color_resetter resetter(std::cout);
     /**ausgabe**/
    
    }
    

    schreiben.

    Danke vielmals Sone.

    Gruessle stuxn


Anmelden zum Antworten