Vou oferecer uma visão ligeiramente diferente sobre isso. Uma biblioteca principal, em muitos casos, é uma excelente ideia!
Se você tiver dois projetos separados, eles deverão estar em dois repositórios de código separados. Agora eles dependem da funcionalidade comum. Vamos considerar, por exemplo, aplicativos de processamento de pacotes. A funcionalidade comum pode incluir:
- Alocadores de memória
- Protocolo de Resolução de Endereço
- Árvore AVL
- Código de serialização para protocolos binários
- Matriz dinâmica
- Lista de hash no estilo do kernel Linux com cabeçalho vinculado individualmente e nós do meio duplamente vinculados
- Tabela de hash
- Código de processamento do cabeçalho TCP / IP
- Lista vinculada regular com cabeça duplamente vinculada e nós médios duplamente vinculados
- Biblioteca de registro
- Diversos (confie em mim, você precisa disso para coisas pequenas e triviais ou o número de módulos diferentes será tão grande quanto 100!)
- Biblioteca de captura de pacotes
- Biblioteca de interface de E / S de pacotes
- Estrutura de dados por pacote
- Fila de bloqueio para comunicação entre threads
- Geradores de números aleatórios
- Árvore vermelho-preta
- Algum tipo de implementação de timer
Agora, aplicativos de processamento de pacotes diferentes podem precisar de um subconjunto diferente deles. Você deve implementar uma biblioteca principal com um repositório de código-fonte ou deve ter 18 repositórios diferentes para cada um desses módulos? Lembre-se de que esses módulos podem ter interdependências; portanto, a maioria desses módulos pode depender, por exemplo, do módulo diverso.
Afirmo que ter uma biblioteca principal é a melhor abordagem. Reduz a sobrecarga de muitos repositórios de código-fonte. Isso reduz o inferno das dependências: uma versão específica dos alocadores de memória pode precisar de uma versão específica do módulo diverso. E se você quiser a versão 1.7 do alocador de memória, dependendo da versão 2.5 e da árvore 1.2 do AVL, dependendo da versão 2.6? Talvez você não consiga vincular diversos 2.5 e 2.6 ao mesmo tempo ao seu programa.
Então, vá em frente e implemente a seguinte estrutura:
- Repositório da biblioteca principal
- Repositório do projeto nº 1
- Repositório do projeto nº 2
- ...
- Repositório do projeto #N
Eu vi que mudar para esse tipo de estrutura a partir da estrutura:
- Repositório do projeto nº 1
- Repositório do projeto nº 2
- ...
- Repositório do projeto #N
Levou à redução da manutenção e ao aumento do compartilhamento de código por meio de mecanismos que não sejam de copipaste.
Também vi projetos usando a seguinte estrutura:
- Repositório de alocadores de memória
- Repositório do protocolo de resolução de endereços
- Repositório da árvore AVL
- Código de serialização para repositório de protocolos binários
- Repositório de matriz dinâmica
- Lista de hash no estilo do kernel Linux com cabeçalho vinculado individualmente e repositório de nós intermediários duplamente vinculado
- Repositório da tabela de hash
- Repositório de código de processamento do cabeçalho TCP / IP
- Lista vinculada regular com cabeçalho duplamente vinculado e repositório de nós médios duplamente vinculado
- Repositório da biblioteca de log
- Repositório diverso (confie em mim, você precisa disso para coisas pequenas e triviais ou o número de módulos diferentes será tão grande quanto 100!)
- Repositório da biblioteca de captura de pacotes
- Repositório da biblioteca de interface de E / S de pacotes
- Repositório da estrutura de dados em pacotes
- Fila de bloqueio para o repositório de comunicação entre encadeamentos
- Repositório de geradores de números aleatórios
- Repositório de árvore vermelho-preto
- Algum tipo de repositório de implementação de timer
- Repositório do projeto nº 1
- Repositório do projeto nº 2
- ...
- Repositório do projeto #N
... e a dependência do inferno e a proliferação de números de repositórios têm sido problemas genuínos.
Agora, você deve usar uma biblioteca de código aberto existente em vez de escrever sua própria? Você precisa considerar:
- Problemas de licença. Às vezes, o mero requisito de dar crédito ao autor na documentação fornecida pode ser demais, pois 20 bibliotecas geralmente terão 20 autores distintos.
- Suporte de versão diferente do sistema operacional
- Dependências da biblioteca específica
- Tamanho da biblioteca específica: é muito grande para a funcionalidade fornecida? Ele fornece muitos recursos?
- É possível vincular estática? A vinculação dinâmica é desejável?
- A interface da biblioteca é o que você deseja? Observe que, em alguns casos, escrever um wrapper para fornecer a interface desejada pode ser mais fácil do que reescrever todo o componente.
- ... e muitas outras coisas que não mencionei nesta lista
Eu costumo usar a regra de que tudo abaixo de 1000 linhas de código que não exija algo além do conhecimento do programador deve ser implementado por conta própria. Nota: as 1000 linhas incluem testes de unidade. Portanto, certamente não defenderei a criação de 1000 linhas de código por conta própria, se exigir 10.000 linhas adicionais para testes de unidade. Para meus programas de processamento de pacotes, isso significa que os únicos componentes externos que usei são:
- Tudo fornecido por uma distribuição Linux padrão, porque são tantas linhas de código que não faz sentido reimplementar o Linux. Partes da reimplementação do Linux também estariam além do meu nível de conhecimento.
- Bison / flex porque a análise LALR está além do meu nível de conhecimento e mais de 1000 linhas de código. Eu certamente poderia escrever um analisador de descida recursivo por conta própria, mas o Bison / flex é tão útil que eu os vejo como úteis.
- Netmap, porque tem mais de 1000 linhas e está além do meu nível de especialização
- Ignorar a implementação do cronômetro baseada em lista do DPDK, porque está além do meu nível de conhecimento, embora seja menor que 1000 linhas de código (embora eu tenha implementações alternativas de cronômetro que não usem listas de ignorados)
Algumas coisas que eu implementei por conta própria porque são simples incluem até coisas como:
- MurMurHash
- SipHash
- Mersenne Twister
... porque implementações personalizadas deles podem permitir inlining pesado, levando a um desempenho aprimorado.
Eu não faço criptografia; se o fizesse, adicionaria algum tipo de biblioteca de criptografia na lista, pois a escrita de algoritmos de criptografia por conta própria pode ser suscetível a ataques de tempo de cache, mesmo que você possa, com um teste de unidade completo, mostrar que são compatíveis com os algoritmos oficiais.