L'esigenza di emettere del testo colorato deriva dalla volontà di rendere più evidente l'esito dei test in GUT.
Poiché le console Windows non supportano le sequenze ANSI[1], la modifica dei colori dei caratteri dev'essere gestita attraverso l'API Win32[2]. L'obiettivo è modificare il colore del testo per mezzo di appositi token:
std::cout << "default text color" << red << "red-colored text" << std::endl;
L'API Win32 definisce due device di output per ogni console, STD_OUTPUT_HANDLE e STD_ERROR_HANDLE rispettivamente. Entrambe corrispondono normalmente allo screen-buffer attivo della console. È facile verificare che – almeno su Windows XP – insistendo sullo stesso screen-buffer, le modifiche fatte su un dispositivo si riflettono sull'altro:
#include <iostream> #include <windows.h> int main() { HANDLE output = GetStdHandle(STD_OUTPUT_HANDLE); std::cout << "this is the cout's default color" << std::endl; SetConsoleTextAttribute(output, FOREGROUND_RED|FOREGROUND_INTENSITY); std::cout << "cout's foreground color has been set to red" << std::endl; HANDLE error = GetStdHandle(STD_ERROR_HANDLE); std::cerr << "this is the cerr's default color" << std::endl; SetConsoleTextAttribute(error, FOREGROUND_BLUE|FOREGROUND_INTENSITY); std::cerr << "cerr's foreground color has been set to blue" << std::endl; std::cout << "is the cout's foreground color still set to blue?" << std::endl; return 0; } // output:
L'idea iniziale prevedeva la definizione di due oggetti – OutputConsole e ErrorConsole – indistinguibili da cout e cerr rispettivamente. Alcuni esperimenti hanno dimostrato che la soluzione più semplice consiste invece nell'overloading dell'operatore std::ostream::operator<< (cfr. [3] per le peculiarità dell'API WriteConsole rispetto alla ridirezione su file).
Considerato l'obiettivo prefissato, i cambiamenti di colore comandati sui due flussi cout e cerr sono stati gestiti attraverso il solo dispositivo STD_OUTPUT_HANDLE:
#include <iostream> #include <windows.h> namespace color { std::ostream& black(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), 0); return os; } std::ostream& navy(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE); return os; } std::ostream& green(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN); return os; } std::ostream& teal(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN|FOREGROUND_BLUE); return os; } std::ostream& maroon(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED); return os; } std::ostream& purple(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_BLUE); return os; } std::ostream& olive(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_GREEN); return os; } std::ostream& silver(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE); return os; } std::ostream& gray(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY); return os; } std::ostream& blue(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_BLUE|FOREGROUND_INTENSITY); return os; } std::ostream& lime(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN|FOREGROUND_INTENSITY); return os; } std::ostream& aqua(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); return os; } std::ostream& red(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_INTENSITY); return os; } std::ostream& fuchsia(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_BLUE|FOREGROUND_INTENSITY); return os; } std::ostream& yellow(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY); return os; } std::ostream& white(std::ostream& os) { SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); return os; } } // namespace color int main() { std::cout << color::black << "black" << std::endl; std::cout << color::navy << "navy" << std::endl; std::cout << color::green << "green" << std::endl; std::cout << color::teal << "teal" << std::endl; std::cout << color::maroon << "maroon" << std::endl; std::cout << color::purple << "purple" << std::endl; std::cout << color::olive << "olive" << std::endl; std::cout << color::silver << "silver" << std::endl; std::cout << color::gray << "gray" << std::endl; std::cout << color::blue << "blue" << std::endl; std::cout << color::lime << "lime" << std::endl; std::cout << color::aqua << "aqua" << std::endl; std::cout << color::red << "red" << std::endl; std::cout << color::fuchsia << "fuchsia" << std::endl; std::cout << color::yellow << "yellow" << std::endl; std::cout << color::white << "white" << std::endl; return 0; } // output:
Quel che manca è la possibilità di ripristinare il colore originale del testo; a ciò si rimedia ricordandosi gli attributi originali della console e predisponendo un nuovo manipulator che li ripristini all'occorrenza:
namespace color { class Console_ { HANDLE handle_; WORD defaultAttrs_; static const WORD mask_ = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY; public: Console_() : handle_(GetStdHandle(STD_OUTPUT_HANDLE)) , defaultAttrs_(getAttrs()) { } void setColor(WORD color) { SetConsoleTextAttribute(handle_, (getAttrs() & ~mask_) | color); } void resetColors() { SetConsoleTextAttribute(handle_, defaultAttrs_); } protected: WORD getAttrs() { CONSOLE_SCREEN_BUFFER_INFO info; GetConsoleScreenBufferInfo(handle_, &info); return info.wAttributes; } }; Console_& theConsole() { static Console_ console; return console; } void setColor_(WORD color) { theConsole().setColor(color); } void resetColors_() { theConsole().resetColors(); } std::ostream& black(std::ostream& os) {SetConsoleTextAttribute( GetStdHandle(STD_OUTPUT_HANDLE), 0);setColor_(0); return os; } // ... std::ostream& reset(std::ostream& os) { resetColors_(); return os; } } // namespace color int main() { std::cout << color::black << "black" << std::endl; // ... std::cout << color::white << "white" << std::endl; std::cout << color::reset << "default" << std::endl; return 0; } // output:
class Console_ { // ... public: Console_() : handle_(GetStdHandle(STD_OUTPUT_HANDLE)) , defaultAttrs_(getAttrs()) { } ~Console_() { resetColors(); } // ... };
Pagina modificata il 05/12/2012