Pergunta

Todas as práticas recomendadas para a limpeza "spaghetti de cabeçalho", que está causando extremamente vezes lento de compilação (Linux / Unix)?

Existe alguma equvalent para "#pragma once" com o GCC?
(Encontrado mensagens conflitantes em relação a este)

Graças.

Foi útil?

Solução

Assumindo que você está familiarizado com "incluem guardas" (#ifdef no início do cabeçalho ..), uma forma adicional de acelerar o tempo de construção é usando externo incluem guardas. Foi discutido em " Large Scale C ++ Software Design ". A ideia é que clássico incluem guardas, ao contrário #pragma uma vez, não sobra você analisar necessário o pré-processador para ignorar o cabeçalho do 2º tempo (ou seja, ele ainda tem para analisar e olhar para o início eo fim do incluem guarda. Com externa incluem guardas de colocar o # ifdef de todo o própria linha #include.

Portanto, parece que isso:

#ifndef MY_HEADER
#include "myheader.h"
#endif

e, claro, dentro do arquivo H você tem o clássico incluem guarda

#ifndef MY_HEADER
#define MY_HEADER

// content of header

#endif

Desta forma, o arquivo myheader.h nem sequer é aberto / analisado pelo pré-processador, e pode poupar-lhe um monte de tempo em grandes projetos, especialmente quando arquivos de cabeçalho sentar em locais remotos compartilhados, como eles às vezes fazem.

novamente, é tudo nesse livro. hth

Outras dicas

Se você quiser fazer uma limpeza completa e tem tempo para fazer isso, então a melhor solução é excluir todos os #includes em todos os arquivos (exceto para os óbvios por exemplo abc.h em abc.cpp) e seguida, compilar o projeto. Adicionar a declaração ou cabeçalho para a frente necessária para corrigir o primeiro erro e repita até que você comple limpa.

Esta não corrigir problemas que podem resultar em incluir questões subjacentes, mas garante que a única inclui são os necessários.

Eu li que GCC considera #pragma once obsoleto, embora ainda #pragma once só pode fazer muito para acelerar as coisas.

Para tentar desvendar o espaguete #include, você pode olhar para doxygen . Ele deve ser capaz de gerar gráficos de cabeçalhos incluídos, o que pode lhe dar uma vantagem em simplificar as coisas. Não me lembro os detalhes improvisadas, mas as características do gráfico pode exigir a instalação GraphViz e dizer doxygen o caminho onde ele pode encontrar dotty.exe do GraphViz.

Outra abordagem que você pode considerar se a compilação é a sua principal preocupação é a criação Precompiled cabeçalhos .

Eu li no outro dia sobre um truque para reduzir dependências de cabeçalho: Escrever um script que irá

  • encontrar todas as instruções # include
  • remover uma instrução de cada vez e recompilações
  • Se a compilação falhar, adicionar o include de volta

No final, você espera acabar com o mínimo de exigido inclui em seu código. Você poderia escrever um script semelhante que re-arranja inclui a descobrir se eles são auto-suficientes, ou exigir outros cabeçalhos para ser incluído antes deles (incluir o cabeçalho primeiro, ver se a compilação falhar, relatá-lo). Isso deve ir alguma maneira de limpar o seu código.

Alguns mais notas:

  • compiladores modernos (gcc entre eles) reconhecer guardas de cabeçalho e otimizar da mesma forma como pragma antes seria apenas abrir o arquivo uma vez.
  • pragma uma vez pode ser problemático quando o mesmo arquivo tem nomes diferentes no sistema de arquivos (ou seja, com soft-links)

  • suportes gcc # pragma uma vez, mas a chama de "obsoleto"
  • pragma uma vez não é suportado por todos os compiladores, e não parte do padrão C

  • não só compiladores pode ser problemático. Ferramentas como IncrediBuild também têm problemas com #pragma once

Richard foi um pouco direita (Por sua solução foi anotado?).

De qualquer forma, todos os cabeçalhos de C / C ++ devem usar interna incluem guardas.

Isto dito, ou:

1 - O seu código legado não é realmente mais mantido, e você deve usar cabeçalhos pré-compilados (que são um hack, mas hey ... Sua necessidade é para acelerar a sua compilação, não refatorar código sem manutenção)

2 - Seu código legado ainda está vivo. Então, você quer usar os cabeçalhos pré-compilados e / ou os guardas / guardas externos para uma solução temporária, mas no final, você precisará remover todos os seus inclui, um .C ou .CPP de cada vez, e compilar cada. C ou .CPP um arquivo ao mesmo tempo, corrigindo sua inclui com frente-declarações ou inclui, quando necessário (ou mesmo quebrar um grande incluir em partes menores para ter certeza de cada arquivo .C ou .CPP terá apenas os cabeçalhos que necessita). De qualquer forma, testar e remover obsoletos inclui faz parte da manutenção de um projeto, então ...

A minha própria experiência com cabeçalhos pré-compilados não era exatamente uma boa, porque metade do tempo, o compilador não poderia encontrar um símbolo que eu tinha definido, e então eu tentei uma completa "limpa / reconstrução", para ter certeza de que não era o cabeçalho da pré-compilado que foi obsoleto. Então, meu palpite é usá-lo para bibliotecas externas você não vai mesmo tocar (como o STL, cabeçalhos API C, Boost, qualquer que seja). Ainda assim, minha própria experiência foi com o Visual C ++ 6, então eu acho que (esperança?) Obteve certo, agora.

Agora, uma última coisa: cabeçalhos deve sempre ser auto-suficiente. Isso significa que se a inclusão de cabeçalhos depende ordem de inclusão, então você tem um problema. Por exemplo, se você pode escrever:

#include "AAA.hpp"
#include "BBB.hpp"

Mas não:

#include "BBB.hpp"
#include "AAA.hpp"

porque a certificação depende AAA, então tudo que você tem é uma dependência que você nunca reconheceu no código. Não reconhecê-lo com uma definição só vai fazer sua compilação um pesadelo. BBB deve incluir AAA, também (mesmo que poderia ser um pouco mais lento: no final, frente-declarações vai de qualquer maneira limpa inútil inclui, por isso, você deve ter um temporizador mais rápido de compilação).

Use um ou mais dos aqueles para acelerar o tempo de compilação

  1. Use Precompiled cabeçalhos
  2. Use um mecanismo de cache (scons por exemplo)
  3. Use um sistema de compilação distribuída (distcc, IncrediBuild ($))

Em cabeçalhos: incluir cabeçalhos somente se você não pode usar a declaração para a frente, mas sempre #include qualquer arquivo que você precisa (incluindo dependências são maus!)

.

Como mencionado em outra resposta, você deve definitivamente usar declarações para a frente sempre que possível. Para meu conhecimento, GCC não tem nada equivalente a # pragma uma vez, que é por isso que eu manter o estilo de moda antiga de incluir guardas.

Obrigado pelas respostas, mas a pergunta é sobre código que inclui rigorosa "incluem ordem" existente etc. A questão é se existem quaisquer ferramentas / scripts para esclarecer o que realmente está acontecendo.

guardas

Header Arent a solução como eles não impedir que o compilador de ler o arquivo inteiro novamente e novamente e ...

PC-Lint irá percorrer um longo caminho para limpar cabeçalhos espaguete. Também vai resolver outros problemas para você também, assim como variáveis ??uninitialised vai invisível, etc.

Como onebyone.livejournal.com comentou em uma resposta à sua pergunta, alguns compiladores apoiar incluem guarda otimização, que a página que eu ligado define da seguinte forma:

A incluem a otimização de guarda é quando um compilador reconhece a interna incluem idioma guarda descrito acima e toma medidas para evitar abrir as múltiplas arquivo vezes. O compilador pode olhar para um arquivo de inclusão, retirar comentários e espaços em branco e descobrir se o todo do arquivo está dentro do incluem guardas. Se for, ele armazena o nome do arquivo e incluem condição de guarda em um mapa. A próxima vez que o compilador é pedido para incluir o arquivo, ele pode verificar a incluir condição de guarda e tomar a decisão se deve ignorar o arquivo ou #include-lo sem a necessidade de abrir o arquivo.

Então, novamente, você já respondeu a essa externo incluem guardas não são a resposta para sua pergunta. Para desembaraçar arquivos de cabeçalho que devem ser incluídos em uma ordem específica, gostaria de sugerir o seguinte:

  • Cada arquivo .c ou .cpp deve #include o arquivo .h correspondente primeiro lugar, e o resto de suas diretrizes #include devem ser classificados em ordem alfabética. Você normalmente irá receber erros de compilação quando isso quebra dependências não declaradas entre arquivos de cabeçalho.
  • Se você tem um arquivo de cabeçalho que define typedefs globais para tipos básicos ou directivas #define globais que são usados ??para a maior parte do código, cada arquivo .h deve #include esse arquivo em primeiro lugar, eo resto de suas diretrizes #include devem ser classificados em ordem alfabética.
  • Quando essas alterações causar erros de compilação, normalmente você vai ter que adicionar uma dependência explícita de um arquivo de cabeçalho para outro, na forma de um #include.
  • Quando essas alterações não causam erros de compilação, que pode causar alterações comportamentais. Esperamos que você tenha algum tipo de conjunto de testes que você pode usar para verificar a funcionalidade do seu aplicativo.

Também soa como parte do problema pode ser que compilações incrementais são muito mais lentos do que deveriam ser. Esta situação pode ser melhorada com declarações para a frente ou um sistema de compilação distribuída, como outros apontaram.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top