Pular para o conteúdo principal

Leitura de configuração em C++

Uma coisa que é comum no ambiente Java e que eu gosto muito são os arquivos de properties. Não é de hoje que eu os uso para configurar aplicações que eu faço.
Eu tinha uma classe de configuração feita na época do C++98 e que hoje, usando, fiquei com vontade de reescrevê-la para ficar com um aspecto mais atual.

Eu pensei nas seguintes regras:

  1. Eu não preciso manter a mesma assinatura;

  2. Vou tentar fazer da forma mais didática possível;

  3. Vou tentar não repetir código;

  4. Eu quero me divertir fazendo isso;

  5. Vamos falar de Policy-based design.




Antes de começar, vamos falar um pouco sobre o Policy-based design. O que é ?
Este é um paradigma onde, basicamente, nós trocamos todas as implementações concretas por tipos genéricos. Isso provê uma grande flexibilidade às bibliotecas que construímos.

Primeira coisa é definir como seria a estrutura que armazenaria as configurações:

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

Aqui vemos as nossas primeiras policies - Key, Value e Store. Elas tem valor default, mas poderiam ser quaisquer outras classes.

Temos a nossa estrutura. Vamos agora ler o arquivo de configuração.
A leitura de um arquivo properties é bem simples. Vamos ler linha a linha
e exibir os valores:

https://gist.github.com/thiagomg/63a5380c349a24a32ad7

Agora já temos as nossas linhas do arquivo sendo mostradas na tela.
Antes de adicionar no config_holder, precisamos fazer um parsing simples para separar o par key=value. Agora começa um pouco de diversão.
Vamos trocar o cout por uma função crack que vai quebrar a linha e gerar um par com chave/valor.

https://gist.github.com/thiagomg/1565db90a7cfa3c6d074

Nós temos aqui um método que vai procurar por um delimitador e retornar os iteradores de início, meio e final, sendo:

  • Início = começo da string (b);

  • Meio = posição do delimitador (pos);

  • Final = final do valor (e).


Ou seja, temos os respectivos intervalos para chave => [b, b+pos) e valor => (b+pos, e). E temos uma função f do tipo AdderFunc que irá receber os iteradores para jogar os valores no mapa.
Repare que não estamos fazendo cópias da string, mas apenas passando os iteradores.

E agora precisamos somente implementar as funções de busca da classe de configuração. Eu pensei nas seguintes funções:

  • get(key) -> retorna valor de key

  • prefix(prefix) -> retorna valores com o prefixo prefix

  • after(prefix) -> retorna o resto das chaves começadas em prefix

  • next_token(prefix, separator) -> retorna o próximo token. Ex: plugin.name


Vamos agora ver a implementação delas.

A função get simplesmente pega o mapa e retorna o valor da chave indicada

https://gist.github.com/thiagomg/97aceb0ab8572a1bb04f

Antes, para implementarmos as outras funções, precisamos fazer uma função auxiliar. Esta função tem como objetivo:

  • iterar pelo mapa e comparar se o prefixo é igual ao passado

  • caso seja, invocar um callback passando o par chave/valor como parâmetro (Assim não teremos cópia)


https://gist.github.com/thiagomg/68035bb92d17c0b8c001

Usando a nossa função auxiliar, a função prefix fica muito mais simples:

https://gist.github.com/thiagomg/9bc40aaf3dd1a2da93c7

As funções after e next_token são pequenas variações da função prefix.
Na primeira, faremos um substring para pegar o resto da chave e na segunda somente o token seguinte.

https://gist.github.com/thiagomg/802fc58c43ee787d3dc0

E vamos mostrar agora como usar a nossa classe de configuração:

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

Como exemplo de uma mudança de policy, vamos trocar o Value de string para my_value.

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

Tivemos basicamente que adicionar uma nova classe e seu parser.

Este post foi um pouco mais extenso, onde apenas arranhamos a superfície do Policy-based design. Espero que tenham gostado.

Sobre Policy-based design:
https://en.wikipedia.org/wiki/Policy-based_design

Fontes:
https://github.com/SimplyCpp/examples/blob/master/config.cpp
https://github.com/SimplyCpp/examples/blob/master/config.h
https://github.com/SimplyCpp/examples/blob/master/example.cfg

 

Comentários

  1. […] Eu gosto bastante desse tipo de design, já usado aqui: http://simplycpp.com/2016/02/05/leitura-de-configuracao-em-c/ […]

    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/