Documentação e Doxygen
Introdução
editarO que é documentação ?
A documentação de um software ou de um código fonte são documentos, textos, imagens ou até mesmos vídeos que descrevem o funcionamento e os detalhes da implementação do mesmo. Os textos, geralmente, estão intrínsecos no código, acompanhando uma classe, função ou um simples trecho de código e eles explicam a lógica por trás de partes complexas. As imagens, geralmente, consistem de diagramas que explicam o funcionamento do código como um todo ou de partes dele, como relações entre classes. Pode haver também manuais detalhados para ajudar o usuário a aproveitar o máximo do produto.
O que é doxygen ?
O Doxygen nada mais é do que um gerador de documentação, ou seja, ele é uma ferramenta utilizada por programadores que desejam automatizar e facilitar o processo de documentação de seus códigos.
A importância da documentação
editarA documentação é uma prática de extrema importância para projetos códigos no geral, pois ela fornece informações essenciais para desenvolvedores, clientes e usuários acarretando em diversos benefícios para o desenvolvimento do trabalho.
Benefícios
editar- Compreensão e Colaboração eficiente
Em um ambiente de trabalho, ou seja, colaborativo, um código fonte bem documentado, além de ser mais legível, facilita a compreensão e o entendimento de cada parte do código para os outros desenvolvedores. Assim, promove a eficiência na comunicação e no trabalho entre os membros da equipe.
- Identificação de problemas
Uma documentação que acompanhe o código permite um melhor entendimento do mesmo, assim facilitando que desenvolvedores encontrem problemas, bugs e erros atuais ou futuramente. Tal técnica também se torna essencial para a manuntenção do código, pois com um melhor entendimento do código ocasiona uma melhor compreensão lógica de sua estrutura facilitando o processo de manutenção no mesmo.
- Acessibilidade
Um código bem documentado faz com que todos os membros do projeto consigam coomprender o funcionamento do software e com isso tomarem melhores decisões para o projeto. Além de facilitar o entendimento e a adaptação de novos membros da equipe com o código.
- Qualidade
Programadores ao documentarem o seu código conseguem pensar minuciosamente cada parte dele, fazendo-o refletir sobre cada variável, função e estrutura lógica podendo alterá-los para obter um código mais limpo e eficiente
Exemplo
editarA partir deste exemplo consegue-se perceber a grande importância de documentar o código. Aqui, como não há nenhum texto ou documentação indicando o que tal algoritmo faz ou o que cada variável representa, torna-se uma tarefa extremamente difícil para outros programadores e desenvolvedores entender a lógica e o objetivo deste algoritmo.
#include<iostream>
#include<vector>
#include<array>
using namespace std;
#define MAXP 32
vector<vector<int>> bss;
vector<array<int, MAXP>> ps;
vector<int> ls, rs;
int c=0;
void dfs(int a, int p) {
ps[a][0] = p;
ls[a] = c++;
for(int b : bss[a])
if(b != p)
dfs(b, a);
rs[a] = c++;
}
int main() {
ios_base::sync_with_stdio(false);cin.tie(NULL);
int qv, qq;
cin >> qv >> qq;
bss.resize(qv);
ps.resize(qv);
ls.resize(qv);
rs.resize(qv);
for(int i=0; i<qv-1; i++) {
int a, b; cin >> a >> b;
a--; b--;
bss[a].push_back(b);
bss[b].push_back(a);
}
dfs(0,0);
for(int j=0; j<MAXP-1; j++) {
for(int i=0; i<qv; i++)
ps[i][j+1] = ps[ps[i][j]][j];
}
for(int i=0; i<qq; i++) {
int a, b; cin >> a >> b;
a--; b--;
if(ls[a] <= ls[b] && rs[b] <= rs[a])
cout << a + 1 << '\n';
else if(ls[b] <= ls[a] && rs[a] <= rs[b])
cout << b + 1 << '\n';
else {
int u = a;
for(int j=MAXP-1; j>=0; j--) {
int v = ps[u][j];
if(ls[v] > ls[b] || rs[b] > rs[v])
u = v;
}
cout << ps[u][0]+1 << '\n';
}
}
return 0;
}
O mesmo código, porém com documentação explicando o algoritmo, suas funções e variáveis. Percebe-se que tais informações são cruciais para que outras pessoas consigam compreender o código com mais facilidade.
/* LCA com Binary Lifting
* é dada uma árvore com qv vértices enraizada no vértice 1
* são dados também qq pares de vértices
* para cada par, encontrar o menor ancestral comum
*/
#include<iostream>
#include<vector>
#include<array>
using namespace std;
// MAXP representa a quantidade de linhas da matriz de "pulos"
// deve ser pelo menos o teto do log do tamanho do maior "pulo" possível no problema
// MAXP >= log(10**9)
#define MAXP 32
vector<vector<int>> bss; // bss[a] representa a lista de adjacência do vértice a
vector<array<int, MAXP>> ps; // matriz de "pulos": ps[a][j] representa o 2**j-ésimo ancestral do vértice a
vector<int> ls, rs; // ls[a] e rs[a] representam respectivamente o tempo inicial e final da DFS no vértice a
int c=0; // contador de tempos para a DFS
void dfs(int a, int p) {
ps[a][0] = p; // incializa o primeiro ancestral de a como o pai na DFS
ls[a] = c++;
for(int b : bss[a])
if(b != p)
dfs(b, a);
rs[a] = c++;
}
int main() {
ios_base::sync_with_stdio(false);cin.tie(NULL);
int qv, qq; // quantidade de vértices e quantidade de consultas
cin >> qv >> qq;
// define o tamanho dos vetores
bss.resize(qv);
ps.resize(qv);
ls.resize(qv);
rs.resize(qv);
// lê as arestas
for(int i=0; i<qv-1; i++) {
int a, b; cin >> a >> b;
a--; b--;
bss[a].push_back(b);
bss[b].push_back(a);
}
// aplica a DFS partindo do vértice zero, fazendo com que a árvore seja enraizada nesse vértice
// aqui, o zero no segundo argumento é muito importante, pois faz com que o ancestral da raiz seja ela mesma
// isso garante que ficaremos na raiz em caso de "pulos" maiores que a altura da árvore
dfs(0,0);
// inicializa a matriz de "pulos"
// cada linha da matriz é construída a partir das informações da linha anterior
for(int j=0; j<MAXP-1; j++) {
for(int i=0; i<qv; i++)
ps[i][j+1] = ps[ps[i][j]][j];
}
// processa as consultas
for(int i=0; i<qq; i++) {
int a, b; cin >> a >> b;
a--; b--;
if(ls[a] <= ls[b] && rs[b] <= rs[a]) // a é ancestral de b
cout << a + 1 << '\n';
else if(ls[b] <= ls[a] && rs[a] <= rs[b]) // b é ancestral de a
cout << b + 1 << '\n';
else { // a e b não são ancestrais um do outro
// usamos a matriz do binary lifting para buscar pelo último ancestral de a que não é um ancestral de b
// durante a busca, representaremos esse ancestral pela variável u
int u = a;
for(int j=MAXP-1; j>=0; j--) { // para cada tamanho de "pulo" começando pelo maior (essa ordem é importante)
int v = ps[u][j]; // v é o 2**j-ésimo ancestral de u
if(ls[v] > ls[b] || rs[b] > rs[v]) // v não é ancestral de b, podemos continuar a busca em v
u = v;
}
// ao final da busca, o menor ancestral comum de a e b é o ancestral direto (pai) de u
cout << ps[u][0]+1 << '\n';
}
}
return 0;
}
Doxygen
editarComo visto acima, o doxygen é um gerador de documentação que abrange diversas linguagens como C++, python, C, C#, Java, PHP, Fortran, Objective-C, etc. Ele possui compatibilidade com sistemas Unix, Windows e Mac Os.
Possui múltiplos formatos para a geração de documentação como HTML, PDF, Latex, Word e XML. Esta flexibilidade permite que o desenvolvedor escolha o melhor formato para o seu projeto. O doxygen também pode gerar representações gráficas como diagramas, facilitando a visualização de classes, suas hierarquias e relações.
O doxygen produz um arquivo de configuração, no qual os usuários podem customizar o processo de geração de documentação. Diversas customizações estão disponíveis como o formato do output, quais arquivos incluir, a linguagem a ser utilizada, etc.
O doxygen é um software livre.
Como utilizar o Doxygen
editarO doxygen extrai a documentação do código diretamente dos comentários no arquivo fonte. Neste exemplo e por padrão utiliza-se o formato de comentários QT para C++, no entanto há diversos outros formatos. Os comentários devem utilizar a seguinte sintaxe:
/*! O comentário do código
* deve estar entre esses
* símbolos
*/
//! Comentário de uma linha
/*! Por padrão os comentários
* são antes das variáveis ou
* funções, porém podemos faze-lo
* de outra forma
*/
int quantidade_rodadas //! Variável que guarda a quantidade de rodadas da partida
Para documentar funções podemos utilizar algumas ferramentas do doxygen que facilitam a documentação da função, dos seus parâmetros e do retorno da função utilizando:
/*!
* \brief: Descrição da função
* \param: Descrição dos parâmetros
* \return Descrição do retorno
*/
Outros comandos estruturais implementados no doxygen são:
/*!
* \struct: Descrição de uma struct.
* \union: Descrição de uma união
* \var: Descrição de uma variavel
* \def: Descrição de um define
* \typedef: Descrição de uma definição utilizando typedef
* \file: Descrição de um arquivo utilizado
* \package: Descrição de pacote em Java
* \interface: Descrição de uma interface
*/
Exemplo de código
editarOs comentários para gerar a documentação pelo doxygen podem ser colocados em qualquer parte do código. Ademais, uma das principais partes do código que devem ser comentadas é o header, pois nele contém diversas funções e variáveis que podem ser utilizadas ao longo do código.
#ifndef Fantasma
#define Fantasma
#include <iostream>
#include <fstream>
#include <random>
class fantasma{
private:
int x_fantasma;
int y_fantasma;
double probabilidade;
char direct_fant;
public:
/*!
*\brief Método: fantasma
* Irá criar a classe fantasma
*/
fantasma();
/*!
*\brief Método ~fantasma
*Irá destroir a classe fantasma.
*/
~fantasma();
/*!
*\brief Método: set_posicao_fantasma
* Irá atribuir as variaveis x_fantasma e y_fantasma as coordenadas x e y (linha e coluna, respectivamente)
* da matriz aonde se encontra o fantasma ou a nova posicao do fantasma.
*
* \param x_fantasma: (int) Representa a coordenada x (linha) da matriz aonde se encontra o fantasma (ou a nova posicao)
* \param y_fantasma: (int) Representa a coordenada y (coluna) da matriz aonde se encontra o fantasma (ou a nova posicao)
*/
void set_posicao_fantasma(int x_fantasma, int y_fantasma);
/*!
*\brief Método: get_x_fantasma
* Irá retornar a coordenada x (linha da matriz - vertical) do fantasma.
*
* \return retorno: (int) Retorna um número que indica a coordenada da linha da matriz aode se
* encontra o fantasma
*/
int get_x_fantasma_();
/*!
*\brief Método: get_y_fantasma
* Irá retornar a coordenada y (coluna da matriz - horizontal) do fantasma.
*
* \return retorno: (int) Retorna um número que indica a coordenada da coluna da matriz aode se
* encontra o fantasma
*/
int get_y_fantasma_();
/*!
*\brief Método: direcao_f
* Irá gerar um número pseudo-aleatório dentro do intervalo [0,1], que irá indicar a posição que
* aquele fantasma vai se mover. Intervalo [0,0.25] irá para a esquerda, [0.25, 0.5] irá para cima,
* [0.5, 0.75] irá para direita e [0.75, 1] irá para baixo.
*
* \return retorno: (char) retorna o caracter que indica qual será o proximo movimento do fantasma
* 'a'-esquerda,'d'-direita,'w'-cima,'s'-baixo.
*/
char direcao_f();
};
#endif
Como gerar a documentação
editarPara usuários de Windows e Mac o Doxygen inclui uma interface gráfica, doxywizard, extremamente prática para gerar a documentação.
Em sistemas Linux, caso deseje uma interface gráfica, deve-se instalar o doxygen-gui. Contudo, pode-se executar tudo pela linha de comando, basta executar os comandos:
sudo apt install doxygen
#Este comando serve para gerar um arquivo de configuração, nele o desenvolvedor pode escolher a configuração desejada
doxygen -g
#Gera a documentação do código
doxygen Doxyfile
Extensões
editarPor se tratar de um software livre, há uma grande comunidade que contribui para o desenvolvimento e aprimoramento do doxygen. Através de inúmeras extensões criadas pela comunidade o doxygen consegue abranger outras linguagens como possuir algumas funcionalidades a mais. Tais extensões podem ser vistas no site do doxygen em https://www.doxygen.nl/helpers.html
Referências
editarhttps://www.doxygen.nl/index.html
https://en.wikipedia.org/wiki/Doxygen
https://pt.wikipedia.org/wiki/Documenta%C3%A7%C3%A3o_de_software