As categorias do iterador do C ++ proíbem a gravação de um adaptador iterador UTF-8?

8

Estou trabalhando em um adaptador iterador UTF-8. Com isso, quero dizer um adaptador que transforma um iterador em uma sequência charou unsigned charem um iterador em uma char32_tsequência. Meu trabalho aqui foi inspirado neste iterador que encontrei online .

No entanto, ao examinar o padrão quando estava iniciando minha própria implementação, cheguei a uma conclusão: não parece possível implementar esse adaptador enquanto ainda está em conformidade com os requisitos que o C ++ impõe aos iteradores.

Por exemplo, você poderia criar um iterador UTF-8 que atenda aos requisitos do InputIterator? Sim, mas apenas enquanto o iterador fornecido não for um InputIterator. Por quê?

Como o InputIterator requer a capacidade de desreferenciar o mesmo iterador mais de uma vez. Você também pode desreferenciar várias cópias desse iterador, desde que todas sejam iguais.

Obviamente, desreferenciar um adaptador iterador UTF-8 requer desreferenciar e potencialmente incrementar o iterador base. E se esse iterador for um InputIterator, não será possível recuperar o valor original depois de incrementá-lo. E o fato de as cópias terem que funcionar significa que você não pode armazenar localmente um char32_tque represente o valor decodificado anteriormente. Você poderia ter feito isso:

auto it = ...
auto it2 = it; //Copies an empty `char32_t`.
*it;           //Accesses base iterator, storing `it.ch`.
*it;           //Doesn't access the base iterator; simply returns `it.ch`.
*it2;          //Cannot access `it.ch`, so must access base iterator.

OK, tudo bem, então você não pode usar InputIterators. Mas e o ForwardIterator? É possível criar um adaptador ForwardIterator que possa adaptar o ForwardIterators sobre seqüências de caracteres UTF-8?

Isso também é problemático, porque a operação *ité necessária para produzir value_type&ou const value_type&. InputIterators pode cuspir qualquer coisa que seja conversível value_type, mas ForwardIteratoré necessário fornecer uma referência real [forward.iterators] /1.3:

if Xé um iterador mutável, referenceé uma referência a T; if Xé um iterador constante, referenceé uma referência aconst T

O único recurso aqui é que todo iterador carregue um a char32_t, que existe apenas para fornecer o armazenamento para essa referência. E mesmo assim, esse valor precisará ser atualizado sempre que a instância do iterador for incrementada e desreferenciada. Isso efetivamente invalida a referência antiga, e o padrão não permite explicitamente isso (a invalidação só pode acontecer quando um iterador é destruído ou se o contêiner diz isso).

O código mencionado acima, encontrado on-line, não é válido devido a isso, pois retorna a uint32_t(pré-C ++ 11 escrito) por valor, em vez de uma referência adequada.

Existe algum recurso aqui? Eu negligenciei algo no padrão ou alguma técnica de implementação que eu poderia usar para contornar esses problemas? Ou isso simplesmente não é possível com a redação atual do padrão?

Nota: o estranho é que parece possível escrever um OutputIterator em conformidade para a conversão UTF-8. Ou seja, um tipo que pega char32_te grava UTF-8 em um charou unsigned charOutputIterator.

Nicol Bolas
fonte
3
É sabido que a redação de ForwardIteratornão se encaixava bem com nenhum tipo de iterador de proxy , como aqueles que tornaram vector<bool>possível. Havia um artigo bem conhecido, escrito em 1999 por Herb Sutter, que explicava por que essa determinação foi feita. Nos tempos modernos, havia uma tendência de repensar essa questão. Acho um escrito por Eric Niebler . Pode haver mais; pode até haver alguns escritos pelo próprio Herb Sutter, em algumas propostas de C ++.
Rwong 01/04
Com o InputIterator, você não pode ler o cache antes de remover a referência ao iterador?
usar o seguinte comando
@immibis: Hum, leia qual cache? A leitura do iterador de entrada antes que o usuário realmente faça referência a ele pode fazer com que eu acesse iteradores inválidos, pois um iterador não sabe necessariamente onde está o final do intervalo. Portanto, se você incrementar um iterador, isso não significa que não há problema em desreferê-lo. Além disso, lembre-se do argumento que fiz sobre a cópia de InputIterators: se você desferir duas cópias do mesmo iterador de entrada, deve obter o mesmo valor.
Nicol Bolas

Respostas:

2

Eu acho que a resposta curta é sim. Um adaptador iterador que decodifique UTF-8 (e mais geralmente, que potencialmente requer vários itens de entrada para produzir um único item de saída) deve ser colocado em camadas sobre um iterador que modela (pelo menos) BidirectionalIterator.

Observe que isso pressupõe que você deseja apenas um iterador constante (ou seja, você só lê UTF-8 da entrada, não grava UTF-8 na coleção subjacente). Se você deseja dar suporte à escrita, as coisas ficam muito mais feias às pressas - a mudança de um valor para outro no nível UTF-32 pode produzir facilmente uma codificação UTF-8 com tamanho diferente; portanto, você deve estar preparado para inserir / excluir itens no meio da coleção subjacente, se você fosse apoiar a gravação.

Jerry Coffin
fonte