Quem arquitetou / projetou o IOStreams do C ++ e ainda seria considerado bem projetado pelos padrões atuais? [fechadas]

127

Primeiro, pode parecer que estou pedindo opiniões subjetivas, mas não é isso que estou procurando. Eu adoraria ouvir alguns argumentos bem fundamentados sobre esse tópico.


Na esperança de ter uma ideia de como deve ser projetada uma estrutura moderna de fluxos / serialização, recentemente consegui uma cópia do livro Standard C ++ IOStreams and Locales de Angelika Langer e Klaus Kreft . Imaginei que, se o IOStreams não fosse bem projetado, ele não teria entrado na biblioteca padrão C ++ em primeiro lugar.

Depois de ler várias partes deste livro, estou começando a ter dúvidas se o IOStreams pode ser comparado com, por exemplo, o STL de um ponto de vista geral da arquitetura. Leia, por exemplo, esta entrevista com Alexander Stepanov (o "inventor" do STL) para aprender sobre algumas decisões de design que entraram no STL.

O que me surpreende em particular :

  • Parece ser desconhecido quem foi responsável pelo design geral do IOStreams (eu adoraria ler algumas informações básicas sobre isso - alguém conhece bons recursos?);

  • Uma vez que você mergulhar abaixo da superfície imediata de iostreams, por exemplo, se você deseja estender iostreams com suas próprias classes, você começa a uma interface com nomes de função de membro bastante enigmáticos e confuso, por exemplo, getloc/ imbue, uflow/ underflow, snextc/ sbumpc/ sgetc/ sgetn, pbase/ pptr/ epptr(e não há exemplos provavelmente ainda piores). Isso torna muito mais difícil entender o design geral e como as peças únicas cooperam. Mesmo o livro que eu mencionei acima não ajuda que muito (IMHO).


Assim, minha pergunta:

Se você tivesse que julgar pelos padrões de engenharia de software de hoje (se há realmente é qualquer acordo geral sobre estes), seria 's C ++ iostreams ainda ser considerada bem concebido? (Eu não gostaria de melhorar minhas habilidades de design de software de algo que geralmente é considerado desatualizado.)

stakx - não está mais contribuindo
fonte
7
Opinião interessante de Herb Sutter stackoverflow.com/questions/2485963/… :) Pena que esse cara deixou SO depois de apenas alguns dias de participação
Johannes Schaub - litb
5
Existe mais alguém que vê uma mistura de preocupações nos fluxos do STL? Um fluxo normalmente é projetado para ler ou gravar bytes e nada mais. Uma coisa que pode ler ou gravar tipos de dados específicos é um formatador (que pode, mas não precisa, usar um fluxo para ler / gravar os bytes formatados). A combinação de ambos em uma classe torna ainda mais complexo a implementação de fluxos próprios.
mmmmmmmm
4
@rsteven, há uma separação dessas preocupações. std::streambufé a classe base para leitura e gravação de bytes e istream/ ostreamé para entrada e saída formatada, tendo um ponteiro std::streambufcomo destino / fonte.
Johannes Schaub - litb
1
@ Litb: Mas é possível mudar o streambuf que é usado pelo stream (formatador)? Então, talvez eu queira usar a formatação STL, mas quero gravar os dados por meio de um streambuf específico?
mmmmmmmm
2
@rstevens,ostream foo(&somebuffer); foo << "huh"; foo.rdbuf(cout.rdbuf()); foo << "see me!";
Johannes Schaub - litb

Respostas:

31

Várias idéias mal concebidas encontraram seu caminho para o padrão: auto_ptr, vector<bool>, valarraye export, só para citar alguns. Portanto, eu não consideraria a presença do IOStreams necessariamente como um sinal de design de qualidade.

Os IOStreams têm um histórico de xadrez. Eles são, na verdade, uma reformulação de uma biblioteca de fluxos anterior, mas foram criados em um período em que muitos dos idiomas C ++ atuais não existiam, portanto os designers não tiveram o benefício da retrospectiva. Um problema que só se tornou aparente com o tempo foi que é quase impossível implementar IOStreams tão eficientemente quanto o stdio de C, devido ao uso abundante de funções virtuais e encaminhamento para objetos de buffer interno, mesmo com a granularidade mais fina, e também graças a alguma estranheza inescrutável na maneira como as localidades são definidas e implementadas. Minha lembrança disso é bastante confusa, admito; Lembro-me de ter sido objeto de intenso debate há alguns anos, sobre o comp.lang.c ++. Moderado.

Marcelo Cantos
fonte
3
Obrigdo por sua contribuição. Vou procurar o comp.lang.c++.moderatedarquivo e postar links na parte inferior da minha pergunta se encontrar algo valioso. - Além disso, ouso discordar de você auto_ptr: Após ler o C ++ excepcional de Herb Sutter , parece uma classe muito útil ao implementar o padrão RAII.
Stakx - não está mais contribuindo
5
@stakx: No entanto, está ficando obsoleto e substituído por unique_ptruma semântica mais clara e mais poderosa.
UncleBens
3
@UncleBens unique_ptrrequer referência rvalue. Então, neste ponto, auto_ptré um ponteiro muito poderoso.
Artyom
7
Mas auto_ptrtem asneira semântica copy / atribuição que o tornam um nicho para dereferencing erros ...
Matthieu M.
5
@TokenMacGuy: não é um vetor e não armazena bools. O que o torna um pouco enganador. ;)
jalf 30/10/10
40

Com relação a quem os projetou, a biblioteca original foi (sem surpresa) criada por Bjarne Stroustrup e depois reimplementada por Dave Presotto. Isso foi redesenhado e reimplementado novamente por Jerry Schwarz para o Cfront 2.0, usando a idéia de manipuladores de Andrew Koenig. A versão padrão da biblioteca é baseada nesta implementação.

Fonte "O Design e Evolução do C ++", seção 8.3.1.

Quuxplusone
fonte
3
@ Neil - nut Qual é a sua opinião sobre o design? Com base em suas outras respostas, muitas pessoas gostariam de ouvir a sua opinião ...
DVK
1
@DVK Acabei de publicar minha opinião como uma resposta separada.
2
Acabei de encontrar uma transcrição de uma entrevista com Bjarne Stroustrup, onde ele menciona alguns trechos da história do IOStreams: www2.research.att.com/~bs/01chinese.html (esse link parece estar temporariamente quebrado no momento, mas você pode tentar Cache de página do Google)
stakx - não está mais contribuindo
2
Link atualizado: stroustrup.com/01chinese.html .
FrankHB 6/05
28

Se você tivesse que julgar pelos padrões atuais de engenharia de software (se houver algum acordo geral sobre eles), os IOStreams do C ++ ainda seriam considerados bem projetados? (Eu não gostaria de melhorar minhas habilidades de design de software de algo que geralmente é considerado desatualizado.)

Eu diria NÃO , por várias razões:

Má manipulação de erros

As condições de erro devem ser relatadas com exceções, não com operator void*.

O antipadrão "objeto zumbi" é o que causa erros como esses .

Má separação entre formatação e E / S

Isso torna os objetos de fluxo desnecessariamente complexos, pois eles precisam conter informações adicionais de estado para formatação, independentemente de você precisar ou não.

Também aumenta as chances de escrever bugs como:

using namespace std; // I'm lazy.
cout << hex << setw(8) << setfill('0') << x << endl;
// Oops!  Forgot to set the stream back to decimal mode.

Se, em vez disso, você escreveu algo como:

cout << pad(to_hex(x), 8, '0') << endl;

Não haveria bits de estado relacionados à formatação e nenhum problema.

Note-se que em linguagens "modernas" como Java, C # e Python, todos os objetos têm um toString/ ToString/ __str__função que é chamada pelas rotinas de I / O. AFAIK, apenas o C ++ faz o contrário, usando stringstreamcomo a maneira padrão de converter em uma string.

Suporte deficiente para i18n

A saída baseada em Iostream divide literais de string em pedaços.

cout << "My name is " << name << " and I am " << occupation << " from " << hometown << endl;

As strings de formato colocam frases inteiras em literais de strings.

printf("My name is %s and I am %s from %s.\n", name, occupation, hometown);

A última abordagem é mais fácil de se adaptar a bibliotecas de internacionalização como o GNU gettext, porque o uso de frases inteiras fornece mais contexto para os tradutores. Se a sua rotina de formatação de string suportar reordenar (como os $parâmetros POSIX printf), também será melhor lidar com as diferenças na ordem das palavras entre os idiomas.

dan04
fonte
4
Na verdade, para i18n, as substituições devem ser identificadas por posições (% 1,% 2, ..), pois uma conversão pode exigir a alteração da ordem dos parâmetros. Caso contrário, eu concordo plenamente - +1.
Peterchen
4
@ Peterchen: É para isso que servem os $especificadores POSIX printf.
Jamesdlin
2
O problema não é o formato das seqüências de caracteres, é que o C ++ possui varargs não tiposseguros.
Dan04 30/10/10
5
A partir do C ++ 11, agora ele possui tipos de segurança seguros.
Mooing Duck
2
IMHO a 'informação extra do estado' é a pior questão. cout é global; anexar sinalizadores de formatação a ele torna esses sinalizadores globais e, quando você considera que a maioria dos usos deles tem o escopo pretendido de algumas linhas, isso é horrível. Seria possível corrigir isso com uma classe 'formatador', que se liga a um ostream, mas mantém seu próprio estado. E, coisas feitas com cout geralmente olhar terrível em comparação com o mesmo feito com printf (quando é possível) ..
Greggo
17

Estou postando isso como uma resposta separada, porque é pura opinião.

Executar entrada e saída (particularmente entrada) é um problema muito, muito difícil; portanto, não é de surpreender que a biblioteca iostreams esteja cheia de itens e coisas que, com uma retrospectiva perfeita, poderiam ter sido melhores. Mas parece-me que todas as bibliotecas de E / S, em qualquer idioma, são assim. Eu nunca usei uma linguagem de programação em que o sistema de E / S fosse uma coisa de beleza que me fez admirar seu designer. A biblioteca iostreams tem vantagens, principalmente sobre a biblioteca de CI / O (extensibilidade, segurança de tipo etc.), mas não acho que alguém a esteja segurando como um exemplo de excelente OO ou design genérico.


fonte
16

Minha opinião sobre o C ++ iostreams melhorou substancialmente com o tempo, principalmente depois que comecei a estendê-los implementando minhas próprias classes de fluxo. Comecei a apreciar a extensibilidade e o design geral, apesar dos nomes ridiculamente pobres de funções de membro, como o que xsputnseja. Independentemente disso, acho que os fluxos de E / S são uma grande melhoria em relação ao C stdio.h, que não possui segurança de tipo e está repleto de grandes falhas de segurança.

Penso que o principal problema dos fluxos de IO é que eles conflitam dois conceitos relacionados, mas de certa forma ortogonais: formatação textual e serialização. Por um lado, os fluxos de E / S são projetados para produzir uma representação textual formatada, legível por humanos, de um objeto e, por outro lado, para serializar um objeto em um formato portátil. Às vezes, esses dois objetivos são o mesmo, mas outras vezes isso resulta em incongruências seriamente irritantes. Por exemplo:

std::stringstream ss;
std::string output_string = "Hello world";
ss << output_string;

...

std::string input_string;
ss >> input_string;
std::cout << input_string;

Aqui, o que obtemos como entrada não é o que originalmente produzimos para o fluxo. Isso ocorre porque o<< operador gera a string inteira, enquanto o >>operador só lê o fluxo até encontrar um caractere de espaço em branco, pois não há informações de tamanho armazenadas no fluxo. Portanto, mesmo que produzimos um objeto de string contendo "hello world", apenas inseriremos um objeto de string contendo "hello". Portanto, embora o fluxo tenha servido a seu objetivo como um recurso de formatação, ele não conseguiu serializar corretamente e depois desserializar o objeto.

Você pode dizer que os fluxos de E / S não foram projetados para serem recursos de serialização, mas se for esse o caso, para que servem realmente os fluxos de entrada ? Além disso, na prática, os fluxos de E / S costumam ser usados ​​para serializar objetos, porque não existem outros recursos de serialização padrão. Considere boost::date_timeouboost::numeric::ublas::matrix , onde, se você produzir um objeto de matriz com o <<operador, obterá a mesma matriz exata ao inseri-lo usando o >>operador. Mas, para conseguir isso, os designers do Boost tiveram que armazenar informações de contagem de colunas e de linhas como dados textuais na saída, o que compromete a exibição legível por humanos. Novamente, uma combinação estranha de recursos de formatação de texto e serialização.

Observe como a maioria dos outros idiomas separa esses dois recursos. Em Java, por exemplo, a formatação é realizada através dotoString() método, enquanto a serialização é realizada através da Serializableinterface.

Na minha opinião, a melhor solução seria a introdução de fluxos baseados em bytes , juntamente com os fluxos padrão baseados em caracteres . Esses fluxos operariam com dados binários, sem preocupação com a formatação / exibição legível por humanos. Eles poderiam ser usados ​​apenas como recursos de serialização / desserialização, para converter objetos C ++ em sequências de bytes portáteis.

Charles Salvia
fonte
obrigado por responder. Eu poderia estar errado sobre isso, mas em relação ao seu último ponto (fluxos baseados em bytes vs. fluxos baseados em caracteres), a resposta (parcial?) Do IOStream não é a resposta para isso: a separação entre buffers de fluxo (conversão, transporte e armazenamento de buffer) e fluxos (formatação / análise)? E você não poderia criar novas classes de fluxo, aquelas destinadas exclusivamente à serialização e desserialização (legíveis por máquina) e outras voltadas exclusivamente para a formatação e análise (legíveis por humanos)?
stakx - não está mais contribuindo com
@ Stakx, sim, e de fato, eu fiz isso. É um pouco mais irritante do que parece, uma vez std::char_traitsque não pode ser portably especializado em tirar uma unsigned char. No entanto, existem soluções alternativas, então acho que a extensibilidade vem ao resgate mais uma vez. Mas acho que o fato de que os fluxos baseados em bytes não são padrão é uma fraqueza da biblioteca.
Charles Salvia
4
Além disso, a implementação de fluxos binários exige que você implemente novas classes de fluxo e novas classes de buffer, pois as preocupações com a formatação não são totalmente separadas std::streambuf. Então, basicamente, a única coisa que você está estendendo é a std::basic_iosclasse. Portanto, há uma linha na qual "estender" passa para o território "completamente reimplementando" e a criação de um fluxo binário a partir das instalações de fluxo de E / S C ++ parece se aproximar desse ponto.
Charles Salvia
bem dito e exatamente o que eu suspeitava. E o fato de C e C ++ se esforçarem muito para não garantir garantias sobre larguras e representações específicas de bits pode realmente se tornar problemático quando se trata de E / S.
stakx - não está mais contribuindo com
" Serializar um objeto em um formato portátil. " Não, eles nunca foram destinados ao apoio que
curiousguy
11

Eu sempre achei C ++ IOStreams mal projetados: sua implementação dificulta a definição adequada de um novo tipo de fluxo. eles também misturam recursos io e recursos de formatação (pense em manipuladores).

pessoalmente, o melhor design e implementação de stream que eu já encontrei está na linguagem de programação Ada. é um modelo de dissociação, uma alegria em criar novos tipos de fluxos, e as funções de saída sempre funcionam independentemente do fluxo usado. isso é graças a um denominador menos comum: você gera bytes para um fluxo e é isso. As funções de fluxo cuidam de colocar os bytes no fluxo, não é tarefa deles, por exemplo, formatar um número inteiro em hexadecimal (é claro, existe um conjunto de atributos de tipo, equivalente a um membro da classe, definido para lidar com a formatação)

Eu gostaria que o C ++ fosse tão simples quanto aos fluxos ...

Adrien Plisson
fonte
O livro que mencionei explica a arquitetura básica do IOStreams da seguinte maneira: Há uma camada de transporte (as classes de buffer do fluxo) e uma camada de análise / formatação (as classes de fluxo). Os primeiros são responsáveis ​​por ler / escrever caracteres de / para um bytestream, enquanto os últimos são responsáveis ​​por analisar caracteres ou serializar valores em caracteres. Isso parece bastante claro, mas não tenho certeza se essas preocupações são realmente claramente separadas na realidade, esp. quando as localidades entram em jogo. - Também concordo com você sobre a dificuldade de implementar novas classes de fluxos.
stakx - não está mais contribuindo com o
"misture recursos io e recursos de formatação" <- O que há de errado nisso? Esse é o objetivo da biblioteca. Em relação à criação de novos fluxos, você deve criar um streambuf em vez de um fluxo e construir um fluxo simples em torno do streambuf.
Billy ONeal
parece respostas a esta pergunta me fez entender algo que eu nunca foi explicado: i deve derivar uma streambuf em vez de um fluxo ...
Adrien Plisson
@takx: Se a camada streambuf fizesse o que você disse, tudo bem. Mas a conversão entre sequência de caracteres e byte é toda misturada com a E / S real (arquivo, console, etc.). Não há como executar a E / S do arquivo sem também fazer a conversão de caracteres, o que é muito lamentável.
Ben Voigt
10

Eu acho que o design do IOStreams é brilhante em termos de extensibilidade e utilidade.

  1. Buffers de fluxo: dê uma olhada nas extensões boost.iostream: crie gzip, tee, copie fluxos em poucas linhas, crie filtros especiais e assim por diante. Não seria possível sem ele.
  2. Integração de localização e integração de formatação. Veja o que pode ser feito:

    std::cout << as::spellout << 100 << std::endl;

    Pode imprimir: "cem" ou até:

    std::cout << translate("Good morning")  << std::endl;

    Pode imprimir "Bonjour" ou "בוקר טוב" de acordo com a localidade imbuída para std::cout!

    Essas coisas podem ser feitas apenas porque os iostreams são muito flexíveis.

Poderia ser feito melhor?

Claro que poderia! De fato, existem muitas coisas que poderiam ser melhoradas ...

Hoje, é bastante doloroso derivar corretamente stream_buffer, não é trivial adicionar informações de formatação adicionais ao fluxo, mas é possível.

Mas, olhando para trás há muitos anos, eu ainda o design da biblioteca era bom o suficiente para trazer muitos presentes.

Como você nem sempre pode ver o quadro geral, mas se você deixar pontos para extensões, isso lhe dará habilidades muito melhores, mesmo em pontos em que você não pensou.

Artyom
fonte
5
Você pode fornecer um comentário sobre por que seus exemplos para o ponto 2 seriam melhores do que simplesmente usar algo como print (spellout(100));e print (translate("Good morning"));Isso parece uma boa idéia, pois isso desacopla a formatação e o i18n da E / S.
Programador
3
Porque pode ser traduzido de acordo com a linguagem imbuída no fluxo. ie french_output << translate("Good morning"):; english_output << translate("Good morning") daria a você: "Bonjour Bom dia"
Artyom
3
A localização é muito mais difícil quando você precisa fazer '<< 'texto' << value' em um idioma, mas '<< valor << 'texto'' em outro - em comparação com printf
Martin Beckett
@ Martin Beckett Eu sei, dê uma olhada na biblioteca Boost.Locale, o que acontece que nesse caso você faz out << format("text {1}") % valuee pode ser traduzido para "{1} translated". Então funciona bem ;-).
Artyom
15
O que "pode ​​ser feito" não é muito relevante. Você é um programador, tudo pode ser feito com bastante esforço. Mas o IOStreams torna muito doloroso alcançar a maior parte do que pode ser feito . E você costuma ter um desempenho ruim por seus problemas.
jalf
2

(Esta resposta é baseada apenas na minha opinião)

Eu acho que os IOStreams são muito mais complexos do que suas funções equivalentes. Quando escrevo em C ++, ainda uso os cabeçalhos cstdio para E / S "antiga", que acho muito mais previsível. Em uma nota lateral, (embora isso não seja realmente importante; a diferença de tempo absoluta é desprezível), os IOStreams provaram em várias ocasiões ser mais lento que o CI / O.

Delan Azabani
fonte
Eu acho que você quer dizer "função" ao invés de "funcional". a programação funcional produz código que é ainda pior quando comparado à programação genérica.
Chris Becke
Obrigado por apontar esse erro; Editei a resposta para refletir a correção.
Delan Azabani
5
O IOStreams quase certamente teria que ser mais lento que o stdio clássico; se eu tivesse a tarefa de projetar uma estrutura de fluxos de E / S extensível e fácil de usar, provavelmente julgaria a velocidade secundária, uma vez que os gargalos reais provavelmente serão a velocidade de E / S do arquivo ou a largura de banda do tráfego de rede.
stakx - não está mais contribuindo
1
Concordo que, para E / S ou rede, a velocidade computacional não importa tanto. No entanto, lembre-se de que o C ++ para conversão numérica / string está usando sstringstream. Eu acho que a velocidade importa, embora seja secundária.
Matthieu M.
1
A E / S do arquivo @stakx e os gargalos da rede são uma função dos custos 'por byte', que são bastante pequenos e diminuem drasticamente por melhorias na tecnologia. Além disso, dado o DMA, essas sobrecargas não retiram o tempo da CPU de outros threads na mesma máquina. Portanto, se você estiver produzindo uma saída formatada, o custo de fazê-lo de forma eficiente versus não pode facilmente ser significativo (pelo menos, não ofuscado pelo disco ou pela rede; mais provavelmente será ofuscado por outro processamento no aplicativo).
Greggo
2

Sempre encontro surpresas ao usar o IOStream.

A biblioteca parece orientada a texto e não binária. Essa pode ser a primeira surpresa: o uso do sinalizador binário em fluxos de arquivos não é suficiente para obter um comportamento binário. O usuário Charles Salvia acima observou isso corretamente: o IOStreams combina aspectos de formatação (onde você deseja uma saída bonita, por exemplo, dígitos limitados para carros alegóricos) com aspectos de serialização (onde você não deseja perda de informações). Provavelmente seria bom separar esses aspectos. Boost.Serialization faz essa metade. Você tem uma função de serialização que direciona para os insersores e extratores, se desejar. Já existe a tensão entre os dois aspectos.

Muitas funções também possuem semânticas confusas (por exemplo, get, getline, ignorar e ler. Algumas extraem o delimitador, outras não; outras também definem um eof). Além disso, alguns mencionam os nomes de funções estranhas ao implementar um fluxo (por exemplo, xsputn, uflow, underflow). As coisas ficam ainda piores quando se usa as variantes wchar_t. O wifstream faz uma tradução para multibyte enquanto o wstringstream não. A E / S binária não funciona imediatamente com wchar_t: você sobrescreve o codecvt.

A E / S com buffer c (ou seja, FILE) não é tão poderosa quanto sua contrapartida em C ++, mas é mais transparente e possui muito menos comportamento contra-intuitivo.

Ainda assim, sempre que tropeço no IOStream, sinto-me atraído por ele como uma mariposa para disparar. Provavelmente, seria bom que um cara realmente inteligente desse uma boa olhada na arquitetura geral.

gast128
fonte
1

Não consigo deixar de responder à primeira parte da pergunta (quem fez isso?). Mas foi respondido em outros posts.

Quanto à segunda parte da pergunta (Bem elaborada?), Minha resposta é um retumbante "Não!". Aqui está um pequeno exemplo que me faz balançar a cabeça em descrença desde anos:

#include <stdint.h>
#include <iostream>
#include <vector>

// A small attempt in generic programming ;)
template <class _T>
void ShowVector( const char *title, const std::vector<_T> &v)
{
    std::vector<_T>::const_iterator iter;
    std::cout << title << " (" << v.size() << " elements): ";
    for( iter = v.begin(); iter != v.end(); ++iter )
    {
        std::cout << (*iter) << " ";
    }
    std::cout << std::endl;
}
int main( int argc, const char * argv[] )
{
    std::vector<uint8_t> byteVector;
    std::vector<uint16_t> wordVector;
    byteVector.push_back( 42 );
    wordVector.push_back( 42 );
    ShowVector( "Garbled bytes as characters output o.O", byteVector );
    ShowVector( "With words, the numbers show as numbers.", wordVector );
    return 0;
}

O código acima produz disparates devido ao design do iostream. Por algumas razões além do meu entendimento, eles tratam os bytes uint8_t como caracteres, enquanto tipos integrais maiores são tratados como números. Qed Design ruim.

Também não há como pensar em consertar isso. O tipo também pode ser um float ou um double ... então, uma conversão para 'int' para fazer o iostream bobo entender que números e não caracteres são o tópico não ajudará.

Depois de receber um voto negativo na minha resposta, talvez mais algumas palavras de explicação ... O design do IOStream é defeituoso, pois não oferece ao programador um meio de declarar COMO um item é tratado. A implementação do IOStream toma decisões arbitrárias (como tratar uint8_t como um caractere, não como um número de bytes). Essa é uma falha do design do IOStream, pois eles tentam alcançar o inatingível.

O C ++ não permite classificar um tipo - o idioma não possui esse recurso. Não existe is_number_type () ou is_character_type () que o IOStream poderia usar para fazer uma escolha automática razoável. Ignorar isso e tentar fugir do palpite é uma falha de design de uma biblioteca.

Admitido, printf () também falharia ao trabalhar em uma implementação genérica "ShowVector ()". Mas isso não é desculpa para o comportamento do iostream. Mas é muito provável que, no caso printf (), ShowVector () seja definido assim:

template <class _T>
void ShowVector( const char *formatString, const char *title, const std::vector<_T> &v );
BitTickler
fonte
3
A culpa não é (puramente) do iostream. Verifique para que uint8_tserve o seu typedef . É realmente um char? Então não culpe o iostreams por tratá-lo como um char.
Martin Ba
E se você deseja obter um número em código genérico, pode usar a num_putfaceta em vez do operador de inserção de fluxo.
Martin Ba
@ Martin Ba Você está certo - os padrões c / c ++ mantêm em aberto quantos bytes um "int sem sinal curto" possui. "char não assinado" é uma idiossincrasia do idioma. Se você realmente deseja um byte, precisa usar um caractere não assinado. O C ++ também não permite impor restrições aos argumentos do modelo - como "apenas números" e, portanto, se eu alterasse a implementação do ShowVector para sua solução num_put proposta, o ShowVector não poderia mais exibir um vetor de strings, certo? ;)
BitTickler
1
@Martin Bla: cppreference menciona que int8_t é um tipo inteiro assinado com largura de exatamente 8 bits.Eu concordo com o autor que é estranho que você obtenha saída de lixo, embora seja tecnicamente explicável pelo typedef e sobrecarga de tipos de caracteres no iostream . Poderia ter sido resolvido tendo um __int8 um tipo true em vez de um typedef.
gast128
Ah, é realmente muito fácil de corrigir: // Correções no std :: ostream que quebrou o suporte para tipos não assinados / assinados / char // e imprime números inteiros de 8 bits como se fossem caracteres. namespace ostream_fixes {inline std :: ostream & operator << (std :: ostream & os, char não assinado i) {return os << static_cast <unsigned int> (i); } inline std :: ostream & operator << (std :: ostream & os, char assinado i) {return os << static_cast <assinado int> (i); }} // namespace ostream_fixes
mcv
1

Os iostreams do C ++ têm muitas falhas, conforme observado nas outras respostas, mas eu gostaria de observar algo em sua defesa.

O C ++ é praticamente único entre os idiomas de uso sério, tornando a entrada e a saída variáveis ​​fáceis para iniciantes. Em outros idiomas, a entrada do usuário tende a envolver coerção de tipo ou formatadores de string, enquanto o C ++ faz com que o compilador faça todo o trabalho. O mesmo é amplamente verdadeiro para a saída, embora o C ++ não seja tão exclusivo nesse aspecto. Ainda assim, você pode executar E / S formatada muito bem em C ++ sem precisar entender classes e conceitos orientados a objetos, o que é pedagogicamente útil e sem ter que entender a sintaxe do formato. Novamente, se você está ensinando iniciantes, é uma grande vantagem.

Essa simplicidade para iniciantes tem um preço, o que pode causar dor de cabeça ao lidar com E / S em situações mais complexas, mas, esperançosamente, nesse ponto o programador tenha aprendido o suficiente para poder lidar com eles ou, pelo menos, ter idade suficiente. para beber.

user2310967
fonte