Domanda

Voglio definire una classe MyStream in modo che:

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

dà in uscita

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

Fondamentalmente, io voglio un "[bla]" inserito all'anteriore, quindi inserite dopo ogni non di terminazione std::endl?

La difficoltà qui NON è la logica di gestione, ma di rilevamento e di sovraccaricare il trattamento dei std::endl.C'è un modo elegante per fare questo?

Grazie!

EDIT:Non ho bisogno di un consiglio su una logica di gestione.Ho bisogno di sapere come individuare/sovraccarico stampa di std::endl.

È stato utile?

Soluzione

Quello che dovete fare è scrivere il proprio stream buffer:
Quando il flusso viene svuotato il buffer di output è prefisso e il contenuto del flusso.

Le seguenti opere perché std::endl causa il seguente.

1) Aggiungere ' ' per il flusso.
2) Chiamate flush() sul torrente
2a) Questo chiamate pubsync() sul buffer del flusso.
2b) si chiama il metodo virtual sync()
2c) eseguire l'Override di questo metodo virtuale per fare il lavoro che si desidera.

#include <iostream>
#include <sstream>

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
>

Altri suggerimenti

Il overload degli operatori del MyStream classe impostare un precedente-stampati-token-era-endl bandiera.

Quindi, se il prossimo oggetto viene stampato, il [blah] può essere inserito nella parte anteriore di esso.

std::endl è una funzione di presa e di restituzione di un riferimento a std::ostream.A rilevarlo è stata spostata nel tuo stream, è necessario sovraccaricare il operator<< tra il tipo e tale funzione:

MyStream& operator<<( std::ostream&(*f)(std::ostream&) )
{
    std::cout << f;

    if( f == std::endl )
    {
        _lastTokenWasEndl = true;
    }

    return *this;
}

Concordato con Neil in linea di principio.

Si desidera modificare il comportamento del buffer, perché questo è il solo modo per estendere iostreams. endl significa questo:

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

widen restituisce un singolo personaggio, quindi non si può mettere la stringa in là. put chiamate putc che non è una funzione virtuale e, solo occasionalmente, ganci per overflow.È possibile intercettare in flush, la quale si chiede il buffer sync.Si avrebbe bisogno di intercettare e modificare tutti i caratteri di fine riga come sono overflowed o manualmente synced e li converte in una stringa.

La progettazione di un override del buffer di classe è problematico perché basic_streambuf si aspetta un accesso diretto alla memoria buffer.Questo impedisce di passare agevolmente richieste di I/O di un preesistente basic_streambuf.Hai bisogno di andare su un arto e si supponga di conoscere il buffer del flusso di classe, e da esso derivano.(cin e cout non sono garantiti per l'uso basic_filebuf, quanto posso dire.) Quindi, basta aggiungere virtual overflow e sync.(Vedere §27.5.2.4.5/3 e 27.5.2.4.2/7.) Eseguire la sostituzione può richiedere spazio aggiuntivo in modo da essere attenti ad allocare in anticipo.

- O -

Dichiarare una nuova endl nel tuo spazio dei nomi, o meglio, un manipolatore che non si chiama endl a tutti!

Invece di tentare di modificare il comportamento di std::endl, probabilmente si dovrebbe creare un filtro streambuf per fare il lavoro.James Kanze ha un esempio che mostra come inserire un timestamp all'inizio di ogni linea di uscita.Si dovrebbe richiedere solo lievi modifiche per cambiare qualsiasi prefisso che si desidera su ogni linea.

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

#include <iostream>

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 si vuole veramente si può controllare l'indirizzo di endl per confermare che si non ottenere alcuni ALTRI void/void funzione, ma non penso che ne vale la pena nella maggior parte dei casi.Spero che aiuta.

Non si può cambiare std::endl - come suggerisce il nome si tratta di una parte della Libreria Standard del C++, e il suo comportamento è stato risolto.È necessario modificare il comportamento del flusso di sé, quando si riceve un fine linea .Personalmente, non avrei pensato che questo vale la pena, ma se si desidera avventurarsi in questa zona, consiglio vivamente di leggere il libro Standard C++ IOStreams & Locales.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top