2015-11-09 7 views
5

Sto facendo un progetto scolastico in cui ho bisogno di cambiare frequentemente il colore del testo. Il target del progetto è l'app Console al momento solo per Windows. Utilizzo di Codeblock con MinGW per il debug. Non sono un noob, ma Intermedio-ish.Come posso rendere la mia funzione appiccicosa per gli operatori di estrazione iostream sovraccarichi

Quindi, utilizzando questo ovunque nel codice era brutto:

SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), __col._colour_code); 

Anche se ho avvolto in una funzione, è ancora ingombrante e brutto, perché non è possibile continuare la catena cout. Hai spezzare la catena, come è necessario chiamare il SetColour in una nuova dichiarazione, ad esempio:

SetColour(GRAY); cout << setcol(PURPLE) << " ID:["; 
SetColour(AQUA); cout << song.GetID(); 
SetColour(GRAY); cout << "]" << " "; 
SetColour(GREEN); cout << song.GetTitle(); 
SetColour(WHITE); cout << " by "; 
SetColour(BRIGHT); cout << song.GetArtist() << "\n"; 

Quello che volevo era una funzionalità simile a quella di setw, setprecision, ecc Così ho aperto la iomainp.h e guardò per alcuni suggerimenti :

struct _Setw { int _M_n; }; 

inline _Setw 
setw(int __n) 
{ return { __n }; } 

template<typename _CharT, typename _Traits> 
inline basic_istream<_CharT, _Traits>& 
operator>>(basic_istream<_CharT, _Traits>& __is, _Setw __f) 
{ 
    __is.width(__f._M_n); 
    return __is; 
} 

template<typename _CharT, typename _Traits> 
inline basic_ostream<_CharT, _Traits>& 
operator<<(basic_ostream<_CharT, _Traits>& __os, _Setw __f) 
{ 
    __os.width(__f._M_n); 
    return __os; 
} 

così ho creato una nuova funzione del mio lavoro in maniera analoga al 100%:

enum Colour { BLACK=0x00, DARK_GREEN=0x02, WHITE=0x07, GRAY, 
       BLUE=0x09, GREEN, AQUA, RED, PURPLE, YELLOW, BRIGHT }; 

struct _colour_struct 
{ 
    uint8_t _colour_code; 
}; 

inline _colour_struct setcolour (Colour colour_foregrnd, Colour colour_backgrnd =BLACK) 
{ 
    uint8_t colour_code = colour_backgrnd; 
    colour_code <<= 4; 
    colour_code |= colour_foregrnd; 
    return { colour_code }; 
} 

namespace std 
{ 
    template<typename _CharT, typename _Traits> 
    inline basic_ostream<_CharT, _Traits>& 
    operator<<(basic_ostream<_CharT, _Traits>& __os, _colour_struct __col) 
    { 
     SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), __col._colour_code); 
     return __os; 
    } 
} 

SORPRESA! (per me) funziona !! es .:

cout << setcolour(GRAY) << " ID:[" << setcolour(AQUA) << song.GetID() << setcolour(GRAY) << "]" << " " 
    << setcolour(GREEN) << song.GetTitle() 
    << setcolour(WHITE) << " by "<< setcolour(BRIGHT) << song.GetArtist() << "\n"; 

Ma consideriamo l'uscita di questo codice:

std::cout << std::setw(20) << setcolour(AQUA) << "1st time" << "\n"; 
std::cout << "2nd time" << "\n"; 
std::cout << "3rd time" << "\n"; 

noti che setw non ha attaccato, è stato azzerato nella seconda riga. Come ?? (Il debug non ha mostrato alcuna chiamata in esecuzione per resettarlo.)

Ma il mio stick DID setcolour per il resto del programma. Perché ?? (Sebbene sia al 100% analogo a setw).

Come posso fare setcolour proprio come setw ??? Ho bisogno di questa funzionalità per rendere il mio programma più pulito e logico.

Inoltre ho trovato questo: Which iomanip manipulators are sticky

Ma le risposte ei commenti solo lì solo io confusi. Apperentemente, setw chiama cout.width (0), ma il debug non ha mostrato tale chiamata, né tale riga di codice è stata trovata in iomanip.h. Inoltre non ho capito la risposta lì. Spiega per favore.

EDIT

forse non ero diretto nella porre la domanda. Come cout.width(0) (in un contesto di setw) viene chiamato ogni volta,

Come posso fare il mio SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0x0) (in un contesto di setcolour) ottenere chiamato ogni volta ??? Come devo affrontare questo problema ??

+0

Che cosa stai usando per visualizzare l'uscita? Un terminale Linux? In tal caso, impostare il colore/carattere è solo una questione di stampare un tag. E il testo dovrebbe mantenere la formattazione finché non si stampa esplicitamente un altro tag. – Rubens

+0

La larghezza per uno stream viene ripristinata dopo ogni scrittura, motivo per cui 'setw' è" antiaderente ". Poiché apparentemente ogni altro iomanip è "appiccicoso", è probabilmente meglio mantenere "setcolour" come è. L'unica altra cosa che posso pensare è scrivere un wrapper per i flussi di output. –

+0

@ AneesAhmed777 Sono solo curioso .. cambia l'ordine di chiamata a 'std :: cout << setcolour (AQUA) << std :: setw (20) <<" 1a volta "<<" \ n ";' causa qualche cambiamento all'output ..? – Minato

risposta

1

La larghezza viene gestita in modo speciale: tutti gli operatori integrati reimpostano la larghezza dopo aver emesso un oggetto. Il fatto che tu non veda una chiamata corrispondente a width(0) nel codice o nel debugger non significa che non ci sia! Potrebbe essere in linea e non raggiungere un punto di interruzione, ad esempio. Rispetto al codice che dovresti esaminare, ad esempio, nell'implementazione di std::num_put<...>: l'operatore di output effettivo non include il codice ma le funzioni do_put().

Per ripristinare altri flag di formattazione, è necessario eseguire il hook in alcune operazioni richiamate dopo ogni operazione di output. Non ci sono molte opportunità, tuttavia: i flussi non supportano la personalizzazione generica dopo ogni oggetto. Di seguito sono elencate le cose che può fare ma vorrei raccomandare di lasciare le bandiere di formattazione appiccicose. È probabilmente un errore per rendere la larghezza speciale:

  • La formattazione numerica avviene attraverso i do_put() funzioni virtuali std::num_put<cT>. Queste funzioni possono essere sovrascritte e, ad esempio, eseguire la normale formattazione delegando all'implementazione della classe base, seguita da qualcos'altro.

    Il problema principale di questo approccio nelle impostazioni è che non funziona con output non numerico. Ad esempio, l'output di stringhe non reimposta nulla.

  • È possibile impostare il flag di formattazione std::unitbuf provocando un flush dopo ogni operatore di output [correttamente scritto]. Lo svuotamento è tradotto in una chiamata di pubsync() e, infine, negli stream std::basic_streambuf<cT>. La funzione sync() può essere sovrascritta. Cioè, l'approccio sarebbe l'installazione di un buffer di flusso personalizzato e il flag std::ios_base::unitbuf quando si impostano i flag Sone che invia l'output allo stream originale e durante la chiamata a sync() reimposta i flag.

    Oltre ad essere un po 'inventato, presenta il problema che non è possibile distinguere un flush originale dal flush automatico (lo scopo principale di srd::ios_base::unitbuf è di ottenere std::cerr svuotato). Inoltre, il reset avviene sul primo std::ostream::sentry in corso di distruzione. Per i valori compositi che è più probabile dopo la formattazione della prima porzione.

  • Il formattatore di colore utilizza comunque un oggetto temporaneo. Il dstructor di questo oggetto potrebbe essere usato per resettare alcuni flag di fornatting. L'operatore di output imposterà le informazioni sul flusso necessarie sull'oggetto corrispondente quando viene "formattato" (probabilmente usando un membro mutable). Ovviamente, questo significa che l'impostazione del formato e dell'output deve essere eseguita dalla stessa dichiarazione. Inoltre, tutti gli output sulla stessa istruzione ricevono lo stesso formato.

Non sono a conoscenza di alcun altro approccio per gestire la formattazione dopo alcuni output. Nessuno degli approcci funziona particolarmente bene. Preferisco usare un approccio di tipo "guard-like" per impostare/disinserire le bandiere piuttosto che cercare di essere intelligente.

+0

Sì, la versione adesiva sembra più utile ora. Le tue soluzioni sono fuori dal mio campionato per ora comunque. Penso che sia meglio usare l'approccio set/unset. – AneesAhmed777

0

Avviso che non si è bloccato, era Reset nella seconda riga. Come ??

Quando si utilizza cout << setcolour(AQUA), si sta apportando una modifica permanente allo stato del programma che verrà utilizzato in seguito.

Questo non è il caso di std::setw(). std::setw() imposta solo la larghezza dell'output successivo e quindi reimposta la larghezza su zero. Vedi http://en.cppreference.com/w/cpp/io/manip/setw per maggiori dettagli. In particolare, le Note:

La proprietà larghezza del flusso sarà azzerato (che significa "non specificato") se una qualsiasi delle seguenti funzioni sono chiamati:

  • ingresso
    • operator>>(basic_istream&, basic_string&)
    • operator>>(basic_ostream&, char*)
  • uscita
    • Overloads 1-7 di basic_ostream::operator<<() (allo stadio 3 di num_put::put())
    • operator<<(basic_ostream&, char) e operator<<(basic_ostream&, char*)
    • operator<<(basic_ostream&, basic_string&)
    • std::put_money (all'interno money_put::put())
    • std::quoted (quando utilizzato con un flusso di output)
+0

OK, questo spiega' setw', ma il debugger NON ha mostrato cout.width (0) in esecuzione, né c'era una riga simile in "iomanip". INOLTRE, COME FACCIO A RIPRISTINARE I MIEI TUTTI !! – AneesAhmed777

Problemi correlati