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