Colored Output in C/C++ (Part 3)

This is part 3 of a 3 part series in printing in color in C/C++

(Continued from Part 2)

cprintf.h

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// (C)2011 Edwards Research Group
// You are licensed to use this work under a CC-BY-SA License.
// See: http://blog.edwards-research.com/about/
//      http://creativecommons.org/licenses/by-sa/3.0/us/
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <stdarg.h>


enum CP_ColorType {BLACK   = 0,
                   RED     = 1,
                   GREEN   = 2,
                   YELLOW  = 3,
                   BLUE    = 4,
                   MAGENTA = 5,
                   CYAN    = 6,
                   WHITE   = 7,
                   UNDEF   = -1};

enum CP_AttrType  {NONE    = 0,
                   BOLD    = 1,
                   DIM     = 2,
                   UNDERLINE = 4,
                   BLINK   = 5,
                   REVERSE = 7};

class ColorSet
{
public:
    CP_ColorType _fg;
    CP_ColorType _bg;
    CP_AttrType  _attr;

    // Default Constructor
    ColorSet(){ set(UNDEF,UNDEF,NONE); }

    // 1 param constructor
    ColorSet(CP_ColorType fg){ set(fg,UNDEF,NONE); }

    // 2 param constructors
    ColorSet(CP_ColorType fg, CP_AttrType attr){ set(fg,UNDEF,attr); }
    ColorSet(CP_ColorType fg, CP_ColorType bg) { set(fg,bg,NONE); }

    // 3 param constructor
    ColorSet(CP_ColorType fg, CP_ColorType bg, CP_AttrType attr){ set(fg,bg,attr); }

    void set(CP_ColorType fg, CP_ColorType bg, CP_AttrType attr)
    {
        _fg   = fg;
        _bg   = bg;
        _attr = attr;
    }

    // Cool Wrapper
    void cprintf(const char * fmt, ...)
    {
        va_list args;
        va_start(args, fmt);

        cp_init();
        vprintf(fmt, args);
        cp_rst();

        va_end(args);
    }

private:
    void cp_init(void)
    {
        if(_bg != -1){
            printf("%c[%d;%d;%dm",27,_attr,(30+_fg),(40+_bg));
        }
        else{
            printf("%c[%d;%dm",27,_attr,(30+_fg));
        }  
    }

    void cp_rst(void)
    {
        printf("%c[%dm", 27, 0);
    }
};

cprintf_test.cpp

////////////////////////////////////////////////////////////////////////////////////////////////////
//
// (C)2011 Edwards Research Group
// You are licensed to use this work under a CC-BY-SA License.
// See: http://blog.edwards-research.com/about/
//      http://creativecommons.org/licenses/by-sa/3.0/us/
//
////////////////////////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>

#include "cprintf.h"

int main(void)
{
    ColorSet alert(RED, BOLD);
    ColorSet warn(YELLOW, UNDERLINE);

    printf("\n");
    printf("Unformatted...\n");
    alert.cprintf("THIS IS AN ALERT!\n");
    warn.cprintf("THIS IS A WARNING.\n");
    printf("\n");

    int i,j;
    for(i=0; i<8; i++)
    {
        for(j=0; j<8; j++)
        {
            if(j == 3 | j == 6){ continue; }
            ColorSet((CP_ColorType)i, (CP_AttrType)j).cprintf("FG=%d,A=%d", i, j);
            printf("    ");
        }
        printf("\n");
    }
    printf("\n");

    warn.cprintf("Much later, I can simply use warn without having to lookup the style I last used.\n");

    // Example Showing real-time definition and overloaded constructors
    ColorSet(YELLOW, RED).cprintf("Yellow on Red...");
    printf("\n");
    ColorSet(BLUE, REVERSE).cprintf("Reversed Blue...");
    printf("\n");


    return 0;
}

Which looks like this in gnome-terminal:

Leveraging some of the benefits of c++, I was able to streamline some of the ColorSet construction that took up an annoying amount of lines with the struct style.

I also changed the #defines to an enum to make the compile-time checking more intelligent.

Lastly, the overloaded constructors allow me to use a 2-parameter constructor for both (FOREGROUND, BACKGROUND) and (FOREGROUND, ATTRIBUTE) style.

Now, if I were to make this production code, I’d hide my member variables, expose getter and setter interfaces (if necessary), re-prefix the enum’s to avoid conflicts, and separate out the implementation into a .cpp file — but this was really just a proof-of-concept thing.

Posted Sunday, April 24th, 2011 under c++, programming, tips and tricks.

Leave a Reply