Pergunta

Eu quero definir uma MyStream classe para que:

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

dá saída

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

Basicamente, eu quero um "[blah]" inserido na parte da frente, em seguida, inserido após cada não terminação std::endl?

A dificuldade aqui não é a gestão de lógica, mas a detecção e sobrecarregar o manuseio de std::endl. Existe uma maneira elegante de fazer isso?

Obrigado!

EDIT: Eu não preciso de conselhos sobre gestão de lógica. Eu preciso saber como detectar / sobrecarga de impressão de std::endl.

Foi útil?

Solução

O que você precisa fazer é escrever seu próprio buffer de fluxo:
Quando o buffer de fluxo é liberado você saída que você prefixar personagens e o conteúdo do fluxo.

As seguintes obras porque std :: endl faz com que o seguinte.

1) Adicionar '\ n' para o fluxo.
2) Chama flush () no fluxo
2a) Este pubsync chamadas () no tampão de fluxo.
2b) Tal exige a sincronização método virtual ()
2c) Substituir esse método virtual para fazer o trabalho que você deseja.

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

Outras dicas

Seus operadores sobrecarregados da classe MyStream tem que definir um-token-era-endl anterior impressa bandeira.

Então, se o objecto seguinte é impresso, o [blah] pode ser inserido em frente do mesmo.

std::endl é uma função que toma e retornando uma referência a std::ostream. Para detectá-lo foi deslocado em sua corrente, você tem que sobrecarregar o operator<< entre o seu tipo e tal função a:

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

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

    return *this;
}

Concordou com Neil por princípio.

Você quer mudar o comportamento do buffer, porque essa é a única maneira de estender iostreams. endl faz isso:

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

widen retorna um único personagem, então você não pode colocar a sua corda lá. put chama putc que não é uma função virtual e só ocasionalmente ganchos para overflow. Você pode interceptar a flush, que chama sync do buffer. Você precisaria interceptar e alterar todos os caracteres de nova linha como eles são overflowed ou manualmente synced e convertê-los para a cadeia.

Projetando uma substituição tampão classe é problemático porque basic_streambuf espera acesso directo à sua memória buffer. Este o impede de facilmente passar pedidos I / O para um basic_streambuf preexistente. Você precisa ir para fora em um membro e suponho que você sabe a classe buffer de fluxo, e dela derivam. (cin e cout não são garantidos para usar basic_filebuf, tanto quanto eu posso dizer). Em seguida, basta adicionar virtual overflow e sync. (Veja §27.5.2.4.5 / 3 e 27.5.2.4.2 / 7.) Realizar a substituição pode exigir espaço adicional que deve ter cuidado para alocar que antes do tempo.

- OU -

Apenas declarar uma nova endl em seu próprio namespace, ou melhor, um manipulador que não é chamado endl em tudo!

Em vez de tentar modificar o comportamento do std::endl, você provavelmente deve criar um streambuf filtragem para fazer o trabalho. James Kanze tem um exemplo mostrando como inserir um timestamp no início de cada linha de saída. Deve exigir a modificação apenas pequenas a mudança que a qualquer prefixo que você deseja em cada linha.

Eu uso ponteiros de função. Parece aterrador para as pessoas que não estão acostumados a C, mas é muito mais eficiente na maioria dos casos. Aqui está um exemplo:

#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 você realmente quiser, você pode verificar o endereço do endl para confirmar que você não está recebendo alguma outra função vazio / vazio, mas eu não acho que vale a pena na maioria dos casos. Espero que ajuda.

Você não pode mudar std::endl - como o próprio nome sugere, é uma parte da biblioteca padrão C ++ e seu comportamento é fixo. Você precisa mudar o comportamento do próprio fluxo, quando recebe um fim de linha. Pessoalmente, eu não teria pensado que isso vale o esforço, mas se você quiser se aventurar nessa área, recomendo vivamente a leitura do livro padrão C ++ iostreams & localidades .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top