Pregunta

Quiero definir una clase para que MyStream:

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

da salida

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

Básicamente, quiero un "[bla]" inserta en la parte delantera, a continuación, se inserta después de cada no termina std::endl?

La dificultad aquí no es la gestión de la lógica, pero la detección y manejo de la sobrecarga de la std::endl. ¿Hay una manera elegante de hacer esto?

Gracias!

EDIT: No necesito asesoramiento sobre la gestión de la lógica. Necesito saber cómo detectar / impresión de la sobrecarga de std::endl.

¿Fue útil?

Solución

Lo que hay que hacer es escribir su propio búfer de la secuencia:
Cuando el búfer de la secuencia que se vacía el prefijo de salida personajes y el contenido de la corriente.

Los siguientes obras porque std :: endl hace lo siguiente.

1) Añadir '\ n' para la corriente.
2) Las llamadas flush () en el flujo
2a) Esto requiere pubsync () en el búfer de la secuencia.
2b) Esto requiere la sincronización método virtual ()
2c) reemplazar este método virtual para hacer el trabajo que desea.

#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
>

Otros consejos

Sus operadores sobrecargados de la clase MyStream tienen que establecer una era anterior endl-impresa-token-bandera.

A continuación, si se imprime el siguiente objeto, la [blah] se puede insertar en frente de ella.

std::endl es una toma de función y devolver una referencia a std::ostream. Para detectar que se desplazó en su corriente, hay que sobrecargar el operator<< entre su tipo y tal función:

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

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

    return *this;
}

convenido con Neil por principio.

¿Quieres cambiar el comportamiento de la memoria intermedia, porque esa es la única manera de extender iostreams. endl hace esto:

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

widen devuelve un solo carácter, por lo que no se puede poner la cadena en ese país. put llama putc que no es una función virtual y sólo ocasionalmente se engancha a overflow. Puede interceptar al flush, que llama sync de la memoria intermedia. Usted tendría que interceptar y cambiar todos los caracteres de nueva línea, ya que se overflowed o manualmente synced y convertirlos a su cadena.

El diseño de una clase de memoria intermedia de anulación es problemático porque basic_streambuf espera que el acceso directo a la memoria integrada. Esto le impide pasar fácilmente las solicitudes de E / S a un basic_streambuf preexistente. Es necesario salir en una extremidad y supongamos que se conoce la clase de búfer de la secuencia, y de ella se derivan. (cin y cout no están garantizados para utilizar basic_filebuf, lo que puedo decir.) A continuación, sólo tiene que añadir virtual overflow y sync. (Ver §27.5.2.4.5 / 3 y 27.5.2.4.2 / 7). Al realizar la sustitución pueden requerir espacio adicional para que tenga cuidado para asignar que antes de tiempo.

- O -

Sólo declarar un nuevo endl en su propio espacio de nombres, o mejor, un manipulador que no se llama endl en absoluto!

En lugar de intentar modificar el comportamiento de std::endl, probablemente debería crear un streambuf filtrado para hacer el trabajo. James Kanze tiene una que muestra cómo insertar una marca de tiempo al comienzo de cada línea de salida. Se debe requerir sólo una pequeña modificación para cambiar eso a cualquier prefijo que desea en cada línea.

Yo uso punteros de función. Suena aterrador para las personas que no están acostumbrados a C, pero es mucho más eficiente en la mayoría de los casos. He aquí un ejemplo:

#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;
}

Si realmente desea puede comprobar la dirección de endl confirmar que no está recibiendo alguna otra función vacío / vacío, pero no creo que vale la pena en la mayoría de los casos. Espero que ayude.

No se puede cambiar std::endl - como su nombre lo indica, es una parte de la biblioteca C ++ estándar y su comportamiento es fijo. Es necesario cambiar el comportamiento de la corriente en sí, cuando recibe un extremo de la línea. En lo personal, yo no habría pensado que esto vale la pena el esfuerzo, pero si quiere aventurarse en esta zona recomiendo encarecidamente la lectura del libro Estándar C ++ iostreams y Locales .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top