c ++ - Atomisk skrive farvet tekst til konsol i Windows

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg skal udskrive for at konsolere noget output med farveindhold. Er det muligt at gøre i Windows atomvist? I Linux er der ansi farver, og det er virkelig meget praktisk at gøre komplekse farvede sætninger. Hvad med windows? Jeg kan gøre følgende:


HANDLE hConsole = GetStdHandle(STD\_OUTPUT\_HANDLE);
printf(" this is how it starts ");
SetConsoleTextAttribute(hConsole, 10);
printf("YES, it should be green ");
SetConsoleTextAttribute(hConsole, 0);
printf("back to standard color
");


Det forekommer mig, at i asynkron app vil denne 3 printf ikke udskrive tekst på samme linje i konsollen. Her kommer 2 løsninger i mit hjerte:


1) Brug mutex til at synkronisere al konsol output, så alle meddelelser vises sekventielt. Synes en overkill løsning til et sådant problem.


2) Brug en eller anden metode til at stoppe konsoludgangen i et stykke tid, udskrive farvet linje og derefter starte output igen.


Så min bekymring er at få farvet linje uden pauser lavet af anden asynkron output. Er det muligt i Windows, og hvad er den bedste tilgang?

Bedste reference


Som @eryksun sagde, brug WriteConsoleOutput i stedet for printf/std :: cout.


Dette er det samme som enhver grafisk applikation. Kun en tråd gør hele skrivningen. Den har en kø, der indeholder en liste over strenge og attributter. Når en tråd ønsker at udskrive noget, skubber den strengen og den relevante attribut i køen. Før udskrivning indstiller skrivetråden attributten og udskriver derefter strengen.


Du skal implementere din egen tråd-sikre kø. Mine er normalt lave volumenudgange, så jeg bruger et array på 256 med en uint8\_t tæller og atommarkører. Der er ingen grænsekontrol: uint8\_t wraps tilbage til 0 efter 255. Cirkulære køer fungerer ret godt.


Du kan oprette en meget enkel klasse til at lave skrivningen. Jeg bruger normalt noget som dette. Scribbler er den klasse, der gør skrivningen til skærmen


class COut : public std::ostringstream
{
   Scribbler* writer;
   WORD attr;
public:
   COut(WORD in\_attr)
   {
      attr = in\_attr;
      writer = Scribbler::Me();
   }
   COut& operator << (std::ostream&(*f)(std::ostream&))
   {
      if (f == std::endl)
      {
         *this << "
";
         writer->Push(attr, str());
         str("");
      }
      else
      {
         *this << f;
      }
      return *this;
   }

   template <typename TT>
   inline COut& operator << (const TT& t)
   {
      (*(std::ostringstream*) this) << t;
      return *this;
   }
};


Dette kan bruges som std :: cout, og det vil gøre alt, hvad du kan gøre med std :: cout. Alternativt kan du skrive en variant af printf, men det er ikke så nemt, når du kommer ind i dybden af ​​passerer std :: arg overalt.


Tråden, der ønsker gul udgang, ville gøre noget lignende


COut yout(FOREGROUND\_RED | FOREGROUND\_GREEN | FOREGROUND\_INTENSITY);
...
yout << "Yellow, yellow dirty fellow" << std::hex << std::setfill('0') << std::setw(4) << 25 << std::endl;


Tråden vil have magenta output


COut mout(FOREGROUND\_RED | FOREGROUND\_BLUE | FOREGROUND\_INTENSITY);
...
mout << ... << std::endl;


Intet bliver udskrevet, indtil en std :: endl er udstedt. Du kan blive helt kreativ med dette med forskellige tråde, der skriver til forskellige rulbare dele af skærmen ved hjælp af SetConsoleWindowInfo. Det vil fungere så længe der kun er en tråd, der gør skrivningen. Alle de andre tråde fortæller bare, hvor man skal skrive udgangen og dens farveattributter.