Pular para o conteúdo principal

Range loops - Escrevendo código seguro

Eu estava olhando um código de um sistema e me deparei com um trecho que me fez torcer o nariz. O código funcionava, mas imediatamente vi dois problemas potenciais:

  1. Bug de int/unsigned int

    1. Um vector::size() - 1

    2. Se size for 0, o resultado será 4294967295 ou 0xFFFFFFFF!



  2. Código confuso e facilmente quebrável

    1. msgs[count] - Não tem offset dinâmico

    2. Um count inválido pode quebrar o programa




https://gist.github.com/thiagomg/fb38cc780769449dc162

Eu suponho que a pessoa que desenvolveu o código fez dessa forma para pegar os itens que estavam no vector, excluindo os itens da borda (0:n), mas como seria essa implementação da forma STL de se programar ?


Três possibilidades


https://gist.github.com/thiagomg/8f910117dcbc4461a7e0

Todas elas tem um problema com relação à implementação original. A implementação original pega o range de [1:size-1). Estas formas que eu apresentei pegam o range de [0:size). Como fazer então ?

A forma mais inocente e com os mesmos problemas da implementação que eu vi seria:

https://gist.github.com/thiagomg/60bf3f9b29f6eb568fd5

Se o tamanho do vector for 0, msgs.cbegin() == msgs.cend(), e teremos um problema, pois msgs.cbegin()+1 estaria apontando para uma região da memória mais adiante da apontada por msgs.cend()-1. Vemos também que os outros exemplos de ranged-for não nos dão a possibilidade de mudar o intervalo com offsets. Qual seria a solução então ?

A definição de um container no qual é possível executar iterações, (grosseiramente) é um objeto que possui as funções begin e end. Então, fiz uma solução interessante para o caso, algo parecido ou no estilo de uma view.

Solução adequada - Range!


Para começar, precisamos de uma estrutura que implemente um range

https://gist.github.com/thiagomg/ffbe23711b8abc5c001d

Agora já temos a nossa estrutura contendo begin e end e dois iteradores. Só falta criarmos uma função que aponte os iteradores para um container definindo o range [1:size-1)

https://gist.github.com/thiagomg/b863b666c01d6ea26e56

Temos agora uma função que retorna de forma segura o range [1:size-1) mesmo quando o vetor é vazio ou contém menos de dois elementos.

Segue abaixo um exemplo de uso da função narrow_range

https://gist.github.com/thiagomg/5e253b31be483d14bae0

Fontes:
https://github.com/SimplyCpp/examples/blob/master/loop1.cpp
https://github.com/SimplyCpp/examples/blob/master/loop2.cpp

Comentários

  1. […] “Um problema que ocorre frequentemente é a escrita de códigos que são facilmente quebráveis. O uso de offsets acaba tornando as iterações frágeis. Este post mostra uma forma de fazer as iterações de forma segura e rápida em C++” [referência: simplycpp.com] […]

    ResponderExcluir

Postar um comentário

Postagens mais visitadas deste blog

Mestre Iota

Iota é a nona letra do alfabeto grego, ela é equivalente à letra i do nosso alfabeto. Por convenção ou hábito, utilizamos a letra i na programação para indicar algum tipo de incrementador, como por exemplo, em um for-loop . https://gist.github.com/fabiogaluppo/a23894ae743f7dd29274 Curiosamente, iota , como identificador, também é utilizado na programação para indicar uma sequência finita e consecutiva de números inteiros, como por exemplo, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]. Inclusive, originalmente na STL existia a função iota , inspirada pela linguagem de programação APL , você pode conferir neste link: http://www.sgi.com/tech/stl/iota.html

Valores Aleatórios Simplificados

A partir do C++ 11, foi introduzido o header <random>  com diversos facilitadores para suporte de geração de números aleatórios. A produção destes números é feita através da combinação de duas categorias de objetos: os geradores e os distribuidores. Os geradores, são responsáveis pela geração dos números, e os distribuidores são responsáveis pela transformação dos números gerados em algum tipo de distribuição de probabilidade.  Como por exemplo, uma distribuição normal (aquela da Gaussiana) ou uma distribuição de Pareto (aquela do 80-20). As opções não faltam, como você pode ver nas referências, por exemplo:   http://www.cplusplus.com/reference/random/ ou  http://en.cppreference.com/w/cpp/header/random .

Policy-based design: log writer

Policy-based design Vamos neste artigo dar mais uma pincelada no Policy-based design . Vamos fazer como exemplo uma classe de log. Como este é só um exemplo, não vamos considerar múltiplos parâmetros no log, mas somente uma string, assim não fugiremos do assunto. Uma das coisas mais importantes neste tipo de design é o desacoplamento. Ele é uma excelente alternativa ao uso de interfaces por duas razões: Não gera chamadas virtuais (ou um nível de indireção em tempo de execução) Duck typing ( https://pt.wikipedia.org/wiki/Duck_typing ) Eu gosto bastante desse tipo de design, já usado aqui: http://simplycpp.com/2016/02/05/leitura-de-configuracao-em-c/