it-swarm-pt.tech

Como devo detectar arquivos #include desnecessários em um projeto C ++ grande?

Estou trabalhando em um grande projeto C++ no Visual Studio 2008 e há muitos arquivos desnecessários #include diretivas. Às vezes o #includes são apenas artefatos e tudo será compilado com eles removidos. Em outros casos, as classes podem ser declaradas posteriormente e o #include pode ser movido para o .cpp Arquivo. Existem boas ferramentas para detectar esses dois casos?

93
shambolic

Embora não revele arquivos de inclusão desnecessários, o Visual studio tem uma configuração /showIncludes (clique com o botão direito do mouse em um .cpp Arquivo, Properties->C/C++->Advanced) que produzirá uma árvore de todos os arquivos incluídos no tempo de compilação. Isso pode ajudar na identificação de arquivos que não precisam ser incluídos.

Você também pode dar uma olhada no idioma pimpl para permitir menos dependências do arquivo de cabeçalho, para facilitar a visualização da sujeira que você pode remover.

47
Eclipse

PC Lint funciona muito bem para isso, e encontra todos os tipos de outros problemas engraçados para você também. Possui opções de linha de comando que podem ser usadas para criar Ferramentas Externas no Visual Studio, mas eu descobri que o addin Visual Lint é mais fácil de trabalhar. Até a versão gratuita do Visual Lint ajuda. Mas dê uma chance ao PC-Lint. Configurá-lo para não emitir muitos avisos leva um pouco de tempo, mas você ficará surpreso com o que acontece.

29
Joe

Existe uma nova ferramenta baseada em Clang, include-what-you-use , que visa fazer isso.

28
Josh Kelley

!!AVISO LEGAL!! Trabalho em uma ferramenta de análise estática comercial (não no PC Lint). !!AVISO LEGAL!!

Existem vários problemas com uma abordagem simples sem análise:

1) conjuntos de sobrecarga:

É possível que uma função sobrecarregada tenha declarações provenientes de arquivos diferentes. Pode ser que a remoção de um arquivo de cabeçalho resulte em uma sobrecarga diferente, em vez de em um erro de compilação! O resultado será uma mudança silenciosa na semântica que pode ser muito difícil de rastrear posteriormente.

2) Especializações de modelos:

Semelhante ao exemplo de sobrecarga, se você possui especializações parciais ou explícitas para um modelo, deseja que todas elas fiquem visíveis quando o modelo for usado. Pode ser que as especializações para o modelo primário estejam em arquivos de cabeçalho diferentes. A remoção do cabeçalho com a especialização não causará um erro de compilação, mas poderá resultar em um comportamento indefinido se essa especialização tiver sido selecionada. (Veja: Visibilidade da especialização de modelo da função C++ )

Conforme apontado por 'msalters', a realização de uma análise completa do código também permite a análise do uso da classe. Ao verificar como uma classe é usada através de um caminho específico de arquivos, é possível que a definição da classe (e, portanto, todas as suas dependências) possa ser removida completamente ou, pelo menos, movida para um nível mais próximo da fonte principal no include árvore.

26
Richard Corden

Não conheço nenhuma dessas ferramentas e pensei em escrever uma no passado, mas acontece que esse é um problema difícil de resolver.

Digamos que seu arquivo de origem inclua a.he b.h; a.h contém #define USE_FEATURE_X e b.h usa #ifdef USE_FEATURE_X. E se #include "a.h" é comentado, seu arquivo ainda pode ser compilado, mas pode não ser o que você espera. Detectar isso programaticamente não é trivial.

Qualquer que seja a ferramenta, isso também precisará conhecer o ambiente de construção. Se a.h se parecer com:

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

Então USE_FEATURE_X é definido apenas se WINNT for definido, portanto, a ferramenta precisaria saber quais diretivas são geradas pelo próprio compilador e quais são especificadas no comando compile em vez de em um arquivo de cabeçalho.

10
Graeme Perrow

Como Timmermans, não estou familiarizado com nenhuma ferramenta para isso. Mas eu conheço programadores que escreveram um script Perl (ou Python) para tentar comentar cada linha de inclusão uma por vez e depois compilar cada arquivo.


Parece que agora Eric Raymond tem uma ferramenta para isso .

O cpplint.py do Google possui uma regra "inclua o que você usa" (entre muitas outras), mas, tanto quanto eu sei, não "inclui" apenas o que você usa. " Mesmo assim, pode ser útil.

9
Max Lybbert

Se você está interessado neste tópico em geral, pode consultar o Lakos ' Design de software C++ em larga escala . É um pouco datado, mas envolve muitos problemas de "design físico", como encontrar o mínimo absoluto de cabeçalhos que precisam ser incluídos. Eu realmente não vi esse tipo de coisa discutida em nenhum outro lugar.

5
Adrian

Experimente Incluir gerente uma tentativa. Ele se integra facilmente no Visual Studio e visualiza seus caminhos de inclusão, o que ajuda a encontrar itens desnecessários. Internamente, ele usa o Graphviz, mas há muitos outros recursos interessantes. E, embora seja um produto comercial, tem um preço muito baixo.

4
Alex

Você pode criar um gráfico de inclusão usando Observador de Dependências de Arquivos Incluídos do C/C++ e encontrar inclusões desnecessárias visualmente.

4
Vladimir

O PC-Lint pode realmente fazer isso. Uma maneira fácil de fazer isso é configurá-lo para detectar arquivos de inclusão não utilizados e ignorar todos os outros problemas. Isso é bastante direto - para ativar apenas a mensagem 766 ("Arquivo de cabeçalho não usado no módulo"), inclua as opções -w0 + e766 na linha de comando.

A mesma abordagem também pode ser usada com mensagens relacionadas, como 964 ("Arquivo de cabeçalho não usado diretamente no módulo") e 966 ("Arquivo de cabeçalho incluído indiretamente, não usado no módulo").

FWIW Eu escrevi sobre isso com mais detalhes em uma postagem do blog na semana passada em http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318 .

3
Anna-Jayne Metcalfe

Se seus arquivos de cabeçalho geralmente começam com

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#endif

(em vez de usar #pragma uma vez), você pode alterar isso para:

#ifndef __SOMEHEADER_H__
#define __SOMEHEADER_H__
// header contents
#else 
#pragma message("Someheader.h superfluously included")
#endif

E como o compilador gera o nome do arquivo cpp que está sendo compilado, isso permitirá que você saiba pelo menos qual arquivo cpp está fazendo com que o cabeçalho seja trazido várias vezes.

3
Sam

Se você deseja remover os desnecessários #include arquivos, a fim de diminuir o tempo de compilação, seu tempo e dinheiro podem ser melhor gastos paralelizando o processo de compilação usando cl.exe/MP , make -j , Xoreax IncrediBuild , distcc/ sorvete etc.

Obviamente, se você já possui um processo de compilação paralelo e ainda está tentando acelerar, limpe seu #include diretivas e remova essas dependências desnecessárias.

2
bk1e

Comece com cada arquivo de inclusão e verifique se cada arquivo de inclusão inclui apenas o que é necessário para se compilar. Qualquer arquivo de inclusão que estiver faltando para os arquivos C++ pode ser adicionado aos próprios arquivos C++.

Para cada arquivo de inclusão e origem, comente cada arquivo de inclusão um de cada vez e veja se ele é compilado.

Também é uma boa idéia classificar os arquivos de inclusão em ordem alfabética e, quando isso não for possível, adicione um comentário.

2
selwyn

Adicionar um ou ambos os #defines a seguir excluirá arquivos de cabeçalho frequentemente desnecessários e poderá melhorar substancialmente os tempos de compilação, especialmente se o código que não estiver usando a API do Windows funcionar.

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

Consulte http://support.Microsoft.com/kb/166474

1
Roger Nelson

Se você ainda não estiver, o uso de um cabeçalho pré-compilado para incluir tudo o que você não vai alterar (cabeçalhos de plataforma, cabeçalhos externos do SDK ou partes estáticas já concluídas do seu projeto) fará uma enorme diferença nos tempos de compilação.

http://msdn.Microsoft.com/en-us/library/szfdksca (VS.71) .aspx

Além disso, embora seja tarde demais para o seu projeto, organizar o projeto em seções e não agrupar todos os cabeçalhos locais em um grande cabeçalho principal é uma boa prática, embora seja necessário um pouco de trabalho extra.

1
anon6439

O último Jetbrains IDE, CLion, mostra automaticamente (em cinza) as inclusões que não são usadas no arquivo atual.

Também é possível ter a lista de todas as inclusões não utilizadas (e também funções, métodos, etc ...) do IDE.

1
Jean-Michaël Celerier

Se você trabalha com o Eclipse CDT, pode experimentar http://includator.com para otimizar sua estrutura de inclusão. No entanto, o Includator pode não saber o suficiente sobre as inclusões predefinidas do VC++ e a configuração do CDT para usar o VC++ com inclusões corretas ainda não está embutida no CDT.

1
PeterSom

Talvez um pouco tarde, mas uma vez encontrei um script WebKit Perl que fazia exatamente o que você queria. Acho que vai precisar de algumas adaptações (não sou muito versado em Perl), mas deve fazer o truque:

http://trac.webkit.org/browser/branches/old/safari-3-2-branch/WebKitTools/Scripts/find-extra-includes

(este é um ramo antigo porque o tronco não possui mais o arquivo)

0
rubenvb

Se houver um cabeçalho específico que você acha que não é mais necessário (por exemplo, string.h), você pode comentar que inclui e, em seguida, colocar abaixo todos os itens:

#ifdef _STRING_H_
#  error string.h is included indirectly
#endif

É claro que os cabeçalhos da interface podem usar uma convenção #define diferente para registrar sua inclusão na memória do CPP. Ou nenhuma convenção; nesse caso, essa abordagem não funcionará.

Depois reconstrua. Existem três possibilidades:

  • Constrói ok. string.h não era crítico para compilação, e a inclusão para ele pode ser removida.

  • O erro # dispara. string.g foi incluído indiretamente de alguma forma Você ainda não sabe se string.h é necessário. Se for necessário, você deve #incluí-lo diretamente (veja abaixo).

  • Você recebe algum outro erro de compilação. string.h era necessário e não está sendo incluído indiretamente; portanto, o include estava correto no início.

Observe que, dependendo da inclusão indireta quando o seu .h ou .c usa diretamente outro .h, é quase certamente um bug: você promete que seu código exigirá apenas esse cabeçalho enquanto for necessário, o que provavelmente não é o que você quis dizer.

As advertências mencionadas em outras respostas sobre cabeçalhos que modificam o comportamento, em vez de declarar coisas que causam falhas na construção também se aplicam aqui.

0
Britton Kerin

Algumas das respostas existentes afirmam que é difícil. Isso é verdade, porque você precisa de um compilador completo para detectar os casos em que uma declaração direta seria apropriada. Você não pode analisar C++ sem saber o que significam os símbolos; a gramática é simplesmente ambígua demais para isso. Você deve saber se um determinado nome nomeia uma classe (pode ser declarada a frente) ou uma variável (não pode). Além disso, você precisa reconhecer o espaço para nome.

0
MSalters