Gestione del sovraccarico di std :: endl?

Voglio definire una class MyStream modo che:

 MyStream myStream; myStream << 1 << 2 << 3 << std::endl << 5 << 6 << std::endl << 7 << 8 << std::endl; 

dà l’output

 [blah]123 [blah]56 [blah]78 

Fondamentalmente, voglio un “[blah]” inserito nella parte anteriore, quindi inserito dopo ogni non terminante std::endl ?

La difficoltà qui NON è la gestione logica, ma il rilevamento e l’overloading della gestione di std::endl . C’è un modo elegante per farlo?

Grazie!

EDIT: non ho bisogno di consigli sulla gestione della logica. Ho bisogno di sapere come rilevare / sovraccaricare la stampa di std::endl .

Quello che devi fare è scrivere il tuo buffer di stream:
Quando il stream del buffer viene svuotato, si stampano i prefissi e il contenuto del stream.

Il seguente funziona perché std :: endl causa quanto segue.

1) Aggiungi “\ n” allo stream.
2) Chiamate flush () sul stream
2a) Questo chiama pubsync () sul buffer del stream.
2b) Questo chiama il metodo virtuale sync ()
2c) Sostituisci questo metodo virtuale per eseguire il lavoro che desideri.

 #include  #include  class MyStream: public std::ostream { // Write a stream buffer that prefixes each line with Plop class MyStreamBuf: public std::stringbuf { std::ostream& output; public: MyStreamBuf(std::ostream& str) :output(str) {} ~MyStreamBuf() { if (pbase() != pptr()) { putOutput(); } } // When we sync the stream with the output. // 1) Output Plop then the buffer // 2) Reset the buffer // 3) flush the actual output stream we are using. virtual int sync() { putOutput(); return 0; } void putOutput() { // Called by destructor. // destructor can not call virtual methods. output << "[blah]" << str(); str(""); output.flush(); } }; // My Stream just uses a version of my special buffer MyStreamBuf buffer; public: MyStream(std::ostream& str) :std::ostream(&buffer) ,buffer(str) { } }; int main() { MyStream myStream(std::cout); myStream << 1 << 2 << 3 << std::endl << 5 << 6 << std::endl << 7 << 8 << std::endl; } > ./a.out [blah]123 [blah]56 [blah]78 > 

I tuoi operatori sovraccaricati della class MyStream devono impostare un flag precedente stampato-token-was-endl.

Quindi, se viene stampato l’object successivo, [blah] può essere inserito di fronte ad esso.

std::endl è una funzione che std::ostream e restituisce un riferimento a std::ostream . Per rilevarlo è stato spostato nel tuo stream, devi sovraccaricare l’ operator<< tra il tuo tipo e tale funzione:

 MyStream& operator<<( std::ostream&(*f)(std::ostream&) ) { std::cout << f; if( f == std::endl ) { _lastTokenWasEndl = true; } return *this; } 

D’accordo con Neil per principio.

Si desidera modificare il comportamento del buffer, poiché questo è l’unico modo per estendere iostream. endl fa questo:

 flush(__os.put(__os.widen('\n'))); 

widen restituisce un singolo carattere, quindi non puoi mettere la stringa lì dentro. put chiamate putc che non è una funzione virtuale e solo occasionalmente si aggancia overflow . Puoi intercettare a flush , che chiama la sync del buffer. Dovresti intercettare e modificare tutti i caratteri di nuova riga perché sono overflow o sync manualmente e convertirli nella stringa.

Progettare una class buffer override è problematico perché basic_streambuf aspetta un accesso diretto alla sua memoria buffer. Ciò impedisce di passare facilmente le richieste di I / O a un basic_streambuf preesistente. È necessario uscire su un arto e supporre di conoscere la class del buffer del stream e derivarne. (Per quanto ne basic_filebuf , cin e cout non sono basic_filebuf utilizzare basic_filebuf .) Quindi aggiungi solo virtual overflow e sync virtual overflow . (Vedere §27.5.2.4.5 / 3 e 27.5.2.4.2 / 7.) L’esecuzione della sostituzione potrebbe richiedere uno spazio aggiuntivo, quindi fare attenzione ad allocarla in anticipo.

– O –

Basta dichiarare un nuovo endl nel proprio namespace, o meglio, un manipolatore che non è affatto chiamato endl !

Invece di tentare di modificare il comportamento di std::endl , probabilmente dovresti creare uno streambuf di filtraggio per fare il lavoro. James Kanze ha un esempio che mostra come inserire un timestamp all’inizio di ogni riga di output. Dovrebbe richiedere solo piccole modifiche per cambiarlo in qualsiasi prefisso desiderato su ogni riga.

Io uso i puntatori di funzione. Sembra terrificante per le persone che non sono abituate a C, ma nella maggior parte dei casi è molto più efficiente. Ecco un esempio:

 #include  class Foo { public: Foo& operator<<(const char* str) { std::cout << str; return *this; } // If your compiler allows it, you can omit the "fun" from *fun below. It'll make it an anonymous parameter, though... Foo& operator<<(std::ostream& (*fun)(std::ostream&)) { std::cout << std::endl; } } foo; int main(int argc,char **argv) { foo << "This is a test!" << std::endl; return 0; } 

Se lo vuoi davvero, puoi controllare l'indirizzo di endl per confermare che non stai ricevendo qualche altra funzione void / void, ma non penso che ne valga la pena nella maggior parte dei casi. Spero che aiuti.

Non è ansible cambiare std::endl – come suggerisce il nome, fa parte della libreria standard C ++ e il suo comportamento è fisso. È necessario modificare il comportamento del stream stesso, quando riceve un fine riga. Personalmente, non avrei pensato che valesse la pena, ma se vuoi avventurarti in quest’area, ti consiglio vivamente di leggere il libro Standard C ++ IOStreams & Locales .