Looking for means to set terminal text color of std::cerr

closed account (42AMoG1T)
I'm currently looking for a way to print messages from std::cerr to the terminal in red in order to draw attention to the user that an error has occurred while using my command line tool.

I'd like this to mostly be supported on OS X and Linux, but hopefully Windows later on. I'm already using Boost in my code if anyone is aware of something in the library I could use.
Last edited on
With Linux (not so sure about OS X, but as they're related I would expect so?) you can use ANSI escape codes to control text color (along with assorted other things). If the terminal supports is there.
http://en.wikipedia.org/wiki/ANSI_escape_code

There are a number of threads on this subject scattered about the cplusplus.com forums:

Linux Console Color (the "\033[" way)
http://www.cplusplus.com/forum/unices/36461/

ANSI Fun
http://www.cplusplus.com/forum/lounge/78225/

...

And chrisname's Hex Viewer uses ANSI escape codes to color its output:

Hex viewer
http://www.cplusplus.com/articles/NwTbqMoL/

Andy

PS Windows does things in a totally different way, using the WinAPI SetConsoleTextAttribute() function, etc.

See, for example, Duaos' post in this thread:
Change color of selected text
http://www.cplusplus.com/forum/general/48596/#msg264748

or search for SetConsoleTextAttribute
Last edited on
closed account (42AMoG1T)
My attempt with this knowledge went something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void setup() {
    #ifdef _WIN32
        #include <windows.h>
        HANDLE handle = GetStdHandle(STD_ERROR_HANDLE);
        SetConsoleTextAttribute(handle, FOREGROUND_RED);
    #else   // Pray for POSIX...
        std::cerr << "\033[0;31m" << std::endl;
    #endif
}

int main(int argc, const char **argv) {
    setup();

   // . . .
}


However, I noticed that this changes the color for all terminal streams, and does so permanently, even after my program has terminated. After all this, I'm assuming there is no way to set only std::cerr to red for only my program while it is running. Are there any other alternatives, or am I forced to write my own personal program function to handle std::cerr messages?
closed account (42AMoG1T)
Solved with this function. However I've only tested in on OS X, so I'm not sure if the Windows segment actually works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>

void print_error(std::string message) {
    #ifdef _WIN32
        #include <windows.h>
        HANDLE handle = GetStdHandle(STD_ERROR_HANDLE);
        int saved = GetConsoleTextAttribute(handle);
        SetConsoleTextAttribute(handle, FOREGROUND_RED);
    #else   // Pray that it's POSIX...
        std::cerr << "\033[0;31m";
    #endif

    std::cerr << message << std::endl;

    #ifdef _WIN32
        SetConsoleTextAttribute(handle, saved);
    #else
        std::cerr << "\033[0m";
    #endif
}
A) I get 8 error like this:

error C2598: linkage specification must be at global scope

when I try to compile your function using MSVC as you're including <windows.h> inside a function; it needs to be included at global scope.

B) There's no GetConsoleTextAttribute(); you have to use GetConsoleScreenBufferInfo() to retrieve the current colour.

C) SetConsoleTextAttribute() doesn't just set the foreground color. For a general solution you really need to handle the background color (and other info) in the character attributes.

Andy
Last edited on
closed account (42AMoG1T)
And we arrive at day two...

Here is what I have now. Again, no Windows computer at my access, which makes this very interesting to say the least. I've saved the user's standard settings into the error_buffer_info object. I'm just having difficulty researching how to set the text attributes to exactly the same except for a red foreground color.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void print_error(std::string message) {
    #ifdef _WIN32
        HANDLE error_handle = GetStdHandle(STD_ERROR_HANDLE);
        CONSOLE_SCREEN_BUFFER_INFO error_buffer_info;
        GenConsoleScreenBufferInfo(error_handle, error_buffer_info);

        SetConsoleTextAttribute(error_handle, FOREGROUND_RED | /* ??? */);
    #else
        std::cerr << "\033[0;31m";
    #endif

    std::cerr << message << std::endl;

    #ifdef _WIN32
        SetConsoleTextAttribute(error_handle, error_buffer_info.wAttributes);
    #else
        std::cerr << "\033[0m";
    #endif
}


In addition, windows.h has been moved at the top of the file, still guarded by #ifdef of course.
Last edited on
The attribute is a bit map, so you just mask it.

Note I'm switching color to yellow if the background is red or magenta, using bright colors, and also a slightly different spelling of 'GenConsoleScreenBufferInfo'!

Andy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void print_error(std::string message) {
    #ifdef _WIN32
        const WORD foregroundMask = FOREGROUND_BLUE  |
                                    FOREGROUND_GREEN |
                                    FOREGROUND_RED   |
                                    FOREGROUND_INTENSITY;

        const WORD backgroundMask = BACKGROUND_BLUE  |
                                    BACKGROUND_GREEN |
                                    BACKGROUND_RED   |
                                    BACKGROUND_INTENSITY;

        WORD errorColor = FOREGROUND_RED | FOREGROUND_INTENSITY; // bright red

        HANDLE handle = GetStdHandle(STD_ERROR_HANDLE);

        CONSOLE_SCREEN_BUFFER_INFO info = {0};
        GetConsoleScreenBufferInfo(handle, &info);

        WORD backgroundColor = info.wAttributes & backgroundMask;
        backgroundColor &= ~BACKGROUND_INTENSITY; // ignore intensity
        // if background is red or magenta use bright yellow instead
        if(    (BACKGROUND_RED == backgroundColor)
            || ((BACKGROUND_RED | BACKGROUND_BLUE) == backgroundColor) )
            errorColor = FOREGROUND_GREEN | BACKGROUND_RED | FOREGROUND_INTENSITY;

        WORD newAttributes = info.wAttributes;
        newAttributes &= ~foregroundMask;
        newAttributes |= errorColor;

        SetConsoleTextAttribute(handle, newAttributes);
    #else   // Pray that it's POSIX...
        std::cerr << "\033[0;31m";
    #endif

    std::cerr << message << std::endl;

    #ifdef _WIN32
        SetConsoleTextAttribute(handle, info.wAttributes);
    #else
        std::cerr << "\033[0m";
    #endif
}
Last edited on
Topic archived. No new replies allowed.