manipulação de std :: endl sobrecarga?
-
19-09-2019 - |
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
.
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 overflow
ed ou manualmente sync
ed 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 .