Benefícios das bibliotecas somente de cabeçalho


98

Quais são os benefícios de uma biblioteca apenas de cabeçalho e por que você a escreveria dessa forma se opondo a colocar a implementação em um arquivo separado?


Principalmente modelos, mas também tornará um pouco mais fácil de distribuir e usar.
BoBTFish

4
Eu gostaria de adicionar as desvantagens de uma biblioteca somente de cabeçalho ao escopo da questão ...
moooeeeep

Quais são as desvantagens que ainda não foram mencionadas?
NebulaFox

7
@moooeeeep: para as desvantagens, você pode querer ler o parágrafo "Pare o código embutido" na página da web dos Projetos Chromium C ++ .
Mr.C64 de

Respostas:


57

Existem situações em que uma biblioteca apenas de cabeçalho é a única opção, por exemplo, ao lidar com modelos.

Ter uma biblioteca apenas de cabeçalho também significa que você não precisa se preocupar com as diferentes plataformas onde a biblioteca pode ser usada. Quando você separa a implementação, geralmente o faz para ocultar os detalhes da implementação e distribuir a biblioteca como uma combinação de cabeçalhos e bibliotecas ( lib, dll's ou .soarquivos). É claro que eles devem ser compilados para todos os sistemas operacionais / versões diferentes para os quais você oferece suporte.

Você também pode distribuir os arquivos de implementação, mas isso significaria uma etapa extra para o usuário - compilar sua biblioteca antes de usá-la.

Claro, isso se aplica caso a caso . Por exemplo, bibliotecas apenas de cabeçalho às vezes aumentamtamanho do código e tempos de compilação.


6
"Ter uma biblioteca somente de cabeçalho também significa que você não precisa se preocupar com as diferentes plataformas onde a biblioteca pode ser usada": somente se você não tiver que manter a biblioteca. Caso contrário, é um pesadelo, com relatórios de bugs que você não pode reproduzir ou testar no material que possui.
James Kanze,

1
Acabei de fazer uma pergunta semelhante sobre os benefícios de desempenho de apenas cabeçalho. Como você pode ver, não há diferença no tamanho do código. No entanto, a implementação de apenas cabeçalho de exemplo foi 7% mais lenta. stackoverflow.com/questions/12290639/…
Homer6

@ Homer6, obrigado por me enviar um ping. Eu nunca realmente medi isso.
Luchian Grigore

1
@LuchianGrigore Não consegui encontrar ninguém que também tivesse. Por isso demorou um pouco para responder. Existem tantos comentários especulativos sobre "aumento do tamanho do código" e "consumo de memória". Finalmente tenho um instantâneo das diferenças, mesmo que seja apenas um exemplo.
Homero 6

@ Homer6. Por que não aumentaria o tamanho do código? Supondo que você crie várias bibliotecas que usam apenas a biblioteca de cabeçalho e, em seguida, seu aplicativo use todas essas bibliotecas, você deve ter várias cópias em vez de vincular a uma única biblioteca compartilhada.
pooya13

60

Benefícios da biblioteca somente de cabeçalho:

  • Simplifica o processo de construção. Você não precisa construir a biblioteca e não precisa especificar a biblioteca compilada durante a etapa de link da construção. Se você tiver uma biblioteca compilada, provavelmente desejará construir várias versões dela: uma compilada com a depuração habilitada, outra com a otimização habilitada e, possivelmente, outra sem símbolos. E talvez ainda mais para um sistema multiplataforma.

Desvantagens de uma biblioteca apenas de cabeçalho:

  • Arquivos de objetos maiores. Cada método embutido da biblioteca que é usado em algum arquivo de origem também obterá um símbolo fraco, definição fora de linha no arquivo de objeto compilado para esse arquivo de origem. Isso torna o compilador mais lento e também o vinculador. O compilador precisa gerar todo esse inchaço e, em seguida, o vinculador precisa filtrá-lo.

  • Compilação mais longa. Além do problema de inchaço mencionado acima, a compilação demorará mais porque os cabeçalhos são inerentemente maiores com uma biblioteca apenas de cabeçalho do que com uma biblioteca compilada. Esses cabeçalhos grandes precisarão ser analisados ​​para cada arquivo de origem que usa a biblioteca. Outro fator é que os arquivos de cabeçalho em uma biblioteca somente de #includecabeçalho têm os cabeçalhos necessários para as definições embutidas, bem como os cabeçalhos que seriam necessários se a biblioteca fosse construída como uma biblioteca compilada.

  • Compilação mais confusa. Você obtém muito mais dependências com uma biblioteca somente de cabeçalho por causa daqueles #includes extras necessários com uma biblioteca somente de cabeçalho. Altere a implementação de alguma função-chave na biblioteca e talvez seja necessário recompilar o projeto inteiro. Faça essa alteração no arquivo de origem de uma biblioteca compilada e tudo o que você precisa fazer é recompilar esse arquivo de origem da biblioteca, atualizar a biblioteca compilada com esse novo arquivo .o e vincular novamente o aplicativo.

  • Mais difícil para o humano ler. Mesmo com a melhor documentação, os usuários de uma biblioteca muitas vezes têm que recorrer à leitura dos cabeçalhos da biblioteca. Os cabeçalhos em uma biblioteca apenas de cabeçalho são preenchidos com detalhes de implementação que atrapalham a compreensão da interface. Com uma biblioteca compilada, tudo o que você vê é a interface e um breve comentário sobre o que a implementação faz, e isso geralmente é tudo o que você deseja. Isso é realmente tudo que você deveria querer. Você não precisa saber os detalhes de implementação para saber como usar a biblioteca.


21
O último ponto realmente não faz sentido. Qualquer documentação razoável incluirá a declaração da função, parâmetros, valores de retorno, etc. e todos os comentários associados. Se você precisar consultar o arquivo de cabeçalho, a documentação falhou.
Thomas

6
@Thomas - Mesmo com as melhores bibliotecas profissionais, muitas vezes me vejo tendo que recorrer à leitura do cabeçalho "fino". Na verdade, se a chamada documentação "boa" for extraída do código mais comentários, normalmente gosto de ler os cabeçalhos. O código mais os comentários me dizem mais do que a documentação gerada automaticamente.
David Hammen,

2
O último ponto não é válido. Os cabeçalhos já estão preenchidos com detalhes de implementação nos membros privados, portanto, não é como se o arquivo cpp ocultasse todos os detalhes de implementação. Além disso, linguagens como C # são essencialmente "apenas cabeçalhos" por design, e o IDE cuida de obscurecer os detalhes ("dobrá-los")
Mark Lakata

2
@Tomas: Concordo, o último ponto é completamente falso. Você pode facilmente manter a interface e a implementação separadas com bibliotecas somente de cabeçalho; você simplesmente tem o cabeçalho da interface #incluir os detalhes de implementação. É por isso que as bibliotecas Boost geralmente incluem um subdiretório (e namespace) chamado detail.
Nemo

4
@Thomas: discordo. O arquivo de cabeçalho é geralmente o primeiro lugar que procuro para obter documentação. Se o cabeçalho for bem escrito, geralmente não há necessidade de documentação externa.
Joel Cornett

14

Eu sei que este é um tópico antigo, mas ninguém mencionou interfaces ABI ou problemas específicos do compilador. Então eu pensei que faria.

Isso é basicamente baseado no conceito de você escrever uma biblioteca com um cabeçalho para distribuir às pessoas ou reutilizar você mesmo em vez de ter tudo em um cabeçalho. Se você está pensando em reutilizar um cabeçalho e arquivos de origem e recompilá-los em todos os projetos, isso não se aplica.

Basicamente, se você compilar seu código C ++ e construir uma biblioteca com um compilador, o usuário tenta usar essa biblioteca com um compilador diferente ou uma versão diferente do mesmo compilador, então você pode obter erros de vinculador ou comportamento de tempo de execução estranho devido à incompatibilidade binária.

Por exemplo, os fornecedores de compiladores costumam alterar sua implementação da STL entre as versões. Se você tem uma função em uma biblioteca que aceita um std :: vector, ela espera que os bytes dessa classe sejam organizados da maneira como foram organizados quando a biblioteca foi compilada. Se, em uma nova versão do compilador, o fornecedor fez melhorias de eficiência em std :: vector, então o código do usuário vê a nova classe que pode ter uma estrutura diferente e passa essa nova estrutura para sua biblioteca. Tudo vai piorando a partir daí ... É por isso que é recomendado não passar objetos STL além dos limites da biblioteca. O mesmo se aplica aos tipos C Run-Time (CRT).

Ao falar sobre o CRT, sua biblioteca e o código-fonte do usuário geralmente precisam ser vinculados ao mesmo CRT. Com o Visual Studio, se você construir sua biblioteca usando o CRT multithread, mas os links do usuário contra o CRT de depuração multithreaded, você terá problemas de link porque sua biblioteca pode não encontrar os símbolos de que precisa. Não me lembro qual era a função, mas para o Visual Studio 2015 a Microsoft criou uma função CRT embutida. De repente, ele não estava no cabeçalho da biblioteca CRT, de modo que as bibliotecas que esperavam encontrá-lo no momento do link não puderam mais fazê-lo e isso gerou erros de link. O resultado foi que essas bibliotecas precisaram ser recompiladas com o Visual Studio 2015.

Você também pode obter erros de link ou comportamento estranho se usar a API do Windows, mas criar com configurações Unicode diferentes para o usuário da biblioteca. Isso ocorre porque a API do Windows tem funções que usam strings e macros Unicode ou ASCII / define quais usam automaticamente os tipos corretos com base nas configurações Unicode do projeto. Se você passar uma string pelo limite da biblioteca que seja do tipo errado, as coisas quebram no tempo de execução. Ou você pode descobrir que o programa não cria links em primeiro lugar.

Essas coisas também são verdadeiras para passar objetos / tipos através dos limites da biblioteca de outras bibliotecas de terceiros (por exemplo, um vetor Eigen ou uma matriz GSL). Se a biblioteca de terceiros alterar seu cabeçalho entre a compilação da biblioteca e a compilação do código pelo usuário, as coisas não funcionarão.

Basicamente, para garantir a segurança, as únicas coisas que você pode ultrapassar os limites da biblioteca são os tipos incorporados e Plain Old Data (POD). Idealmente, qualquer POD deve estar em estruturas definidas em seus próprios cabeçalhos e não depender de cabeçalhos de terceiros.

Se você fornecer uma biblioteca apenas de cabeçalho, todo o código será compilado com as mesmas configurações do compilador e com os mesmos cabeçalhos, de modo que muitos desses problemas desaparecem (fornecendo a versão de terceiros parcialmente bibliotecas que você e seu usuário usa são compatíveis com API).

No entanto, existem aspectos negativos que foram mencionados acima, como o aumento do tempo de compilação. Além disso, você pode estar administrando uma empresa, portanto, não pode querer entregar todos os detalhes de implementação do seu código-fonte a todos os seus usuários, caso um deles o roube.


8

O principal "benefício" é que requer que você forneça o código-fonte, então você acabará com relatórios de erros em máquinas e com compiladores dos quais nunca ouviu falar. Quando a biblioteca é inteiramente de modelos, você não tem muita escolha, mas quando você tem a escolha, apenas o cabeçalho é geralmente uma escolha de engenharia ruim. (Por outro lado, é claro, o cabeçalho significa apenas que você não precisa documentar nenhum procedimento de integração.)


0

Inlining pode ser feito por Link Time Optimization (LTO)

Gostaria de destacar isso, pois diminui o valor de uma das duas principais vantagens das bibliotecas somente de cabeçalho: "você precisa de definições em um cabeçalho para embutir".

Um exemplo concreto mínimo disso é mostrado em: Link-time optimization and inline

Então, você apenas passa um sinalizador e o inlining pode ser feito em arquivos de objetos sem nenhum trabalho de refatoração, não há mais necessidade de manter as definições nos cabeçalhos para isso.

LTO pode ter suas próprias desvantagens também, no entanto: há uma razão para não usar a otimização de tempo de link (LTO)?

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.