A detecção de # supérfluos inclui em C / C ++?

289

Costumo achar que a seção de cabeçalhos de um arquivo fica cada vez maior o tempo todo, mas nunca fica menor. Ao longo da vida útil de um arquivo de origem, as classes podem ter sido movidas e refatoradas e é muito possível que existam algumas #includesque não precisem mais estar lá. Deixá-los lá apenas prolonga o tempo de compilação e adiciona dependências de compilação desnecessárias. Tentar descobrir quais ainda são necessários pode ser bastante entediante.

Existe algum tipo de ferramenta que pode detectar diretivas supérfluas #include e sugerir quais eu posso remover com segurança?
Talvez o fiapo faça isso?

shoosh
fonte
1
Veja também: stackoverflow.com/questions/74326/…
Eclipse
1
A questão vinculada parece resolver apenas o problema no Windows, usando o Visual Studio em particular.
D'Nabre
7
Votação para reabrir isso, pois o duplicado é sobre o uso do Visual Studio, especificamente.
Tirou Dormann

Respostas:

42

Não é automático, mas o doxygen produzirá diagramas de dependência para #includedarquivos. Você precisará examiná-los visualmente, mas eles podem ser muito úteis para obter uma imagem do que está usando o quê.

Albert
fonte
5
Essa é uma ótima maneira de ver cadeias. Ver A -> B -> C -> D e A -> D revela imediatamente a redundância.
Tom
34
@ Tom: Essa é uma idéia horrível: por um lado, não mostra se essas inclusões são necessárias ou não e, em segundo lugar, a lista de inclusões não deve depender de inclusões indiretas que podem mudar no futuro (inclusões redundantes geralmente não são tão grande problema de qualquer maneira, graças à inclusão de guardas e magia do compilador), mas em quais classes / funções são realmente usadas no arquivo (seu compilador não deve passar por milhares de linhas de código de modelo que nem são instanciadas)
MikeMB
@ albert, você pode incluir capturas de tela disso e descrever brevemente onde clicar na saída doxygen?
Gabriel Staples
@GabrielStaples Não é minha resposta, então não quero adicionar informações a ela. Corrigi apenas o link (como o local de hospedagem a que ele se referia parou / apreendeu para ser usado).
albert
177

O cppclean do Google (links para: download , documentação ) pode encontrar várias categorias de problemas de C ++ e agora pode encontrar #includes supérfluos.

Há também uma ferramenta baseada em Clang, que inclui o que você usa , que pode fazer isso. o include-you-use pode até sugerir declarações avançadas (para que você não precise #incluir muito) e, opcionalmente, limpar seus #includes para você.

As versões atuais do Eclipse CDT também têm essa funcionalidade embutida: ir no menu Origem e clicar em Organizar inclui, classificar seus # include's, adicionar cabeçalhos que o Eclipse achar que você está usando sem incluí-los diretamente e comentar os cabeçalhos que ele não usar acho que você precisa. Esse recurso não é 100% confiável, no entanto.

Josh Kelley
fonte
2
Faz agora. Estou apenas começando a usá-lo. Veja minha nota aqui. stackoverflow.com/questions/1301850/…
Possibilidade
1
O repositório cppclean é baixo, agora você pode obtê-lo aqui: bitbucket.org/robertmassaioli/cppclean (o site original ainda é útil para alguns exemplo de uso embora)
Nick
3
Atualizei o link para um fork cppclean mantido: github.com/myint/cppclean
BenC
1
Observe que o cppclean parece encontrá-los apenas nos arquivos de cabeçalho, e não nos arquivos cpp, do documento: "# desnecessário inclui nos arquivos de cabeçalho".
Zitrax
1
@ wizurd - Não acompanhei os desenvolvimentos recentes no Eclipse CDT, mas acho que não. Iwyu é completo e relativamente lento. A análise do Eclipse CDT é rápida (interativa) e, quando testei, menos precisa.
21718 Josh Kelley
65

Verifique também o que você usa , que resolve um problema semelhante.

Tzafrir
fonte
6
IMHO esta resposta precisa de muito mais votos, uma vez que as dobras são elaboradas, a ferramenta IWYU do Google será a ferramenta definitiva para esta tarefa.
111111 Dan Olson
5
sudo apt-get install iwyu
Andrew Wagner
Parece ótimo - com duas cavaets 1) última atualização em fevereiro de 2106 2) o próprio Gogole o usa apenas para C ++, não para C, solicitado pelo OP.
Mawg diz que restabelece Monica em
Você pode explicar um pouco como o usuário deve usá-lo? O README não é muito claro sobre o que contém a saída do script python.
bobo da corte do rei
Estou usando isso, mas nem sempre é 100% correto. Talvez 70% das vezes dê as sugestões corretas.
InQusitive 27/02
25

O problema com a detecção de inclusões supérfluas é que não pode ser apenas um verificador de dependência de tipo. Uma inclusão supérflua é um arquivo que não fornece nada de valor à compilação e não altera outro item do qual outros arquivos dependem. Existem várias maneiras de um arquivo de cabeçalho alterar uma compilação, digamos, definindo uma constante, redefinindo e / ou excluindo uma macro usada, adicionando um espaço para nome que altera a pesquisa de um nome de alguma forma na linha. Para detectar itens como o espaço para nome, você precisa muito mais do que um pré-processador, na verdade você quase precisa de um compilador completo.

O Lint é mais um verificador de estilo e certamente não terá esse recurso completo.

Eu acho que você encontrará a única maneira de detectar uma inclusão supérflua é remover, compilar e executar suítes.

JaredPar
fonte
8
Nada disso será um problema se os arquivos de inclusão forem bem definidos. Se você precisar incluir o arquivo A antes do arquivo B, estará fazendo errado (e trabalhei em projetos nos quais eles fizeram errado).
David Thornley
9
@ David, sim, mas isso depende dos anos dos desenvolvedores antes de você fazer isso corretamente. Posso dizer com grande certeza que as chances de isso acontecer favorecem a casa, não você :(
JaredPar
Sim, mas geralmente descubro isso ao modificar um programa e, de repente, tenho um erro de compilação (se tiver sorte) ou um bug obscuro. Isso parece manter os arquivos #include honestos, pelo menos a longo prazo.
21420 David Thornley
Eu diria exatamente o contrário. Tudo que você precisa é de um verificador de dependência de tipo. Pode não ser compilado após a organização das inclusões, mas esses são problemas que devem ser tratados de qualquer maneira.
Benoît
1
@ Benoit, você estaria ignorando uma classe de problemas que compilam, mas que mudam semanticamente o significado do seu programa. Considere como um #define em um arquivo pode alterar um #if branch em outro. A remoção de um cabeçalho ainda pode permitir que isso compilar com resultados diferentes
JaredPar
15

Eu pensei que o PCLint faria isso, mas já faz alguns anos desde que eu olhei para ele. Você deveria conferir.

Eu olhei para este blog e o autor falou um pouco sobre como configurar o PCLint para encontrar inclusões não utilizadas. Pode valer a pena dar uma olhada.

itsmatt
fonte
Boa descoberta! Vou ter que usar isso.
crashmstr
4
Uso o PCLint regularmente e ele me mostra cabeçalhos não utilizados. Tenho cuidado para comentar o #include cabeçalho e re-compilação para ter certeza de que o cabeçalho é verdadeiramente sem uso ...
Harold Bamford
Obrigado pela confirmação, Harold.
#
5
muito caro. não é uma ferramenta viável para as massas.
7

O navegador de refatoração do CScout pode detectar diretivas de inclusão supérfluas no código C (infelizmente não C ++). Você pode encontrar uma descrição de como isso funciona neste artigo de jornal.

Diomidis Spinellis
fonte
5

Você pode escrever um script rápido que apague uma única diretiva #include, compile os projetos e registre o nome na #include e o arquivo do qual foi removido no caso em que não ocorreram erros de compilação.

Deixe funcionar durante a noite e, no dia seguinte, você terá uma lista 100% correta dos arquivos de inclusão que você pode remover.

Às vezes, a força bruta simplesmente funciona :-)


edit: e às vezes não :-). Aqui estão algumas informações dos comentários:

  1. Às vezes, você pode remover dois arquivos de cabeçalho separadamente, mas não os dois juntos. Uma solução é remover os arquivos de cabeçalho durante a execução e não trazê-los de volta. Ele encontrará uma lista de arquivos que você pode remover com segurança, embora possa haver uma solução com mais arquivos para remover, que esse algoritmo não encontrará. (é uma pesquisa gananciosa no espaço de inclusão de arquivos a serem removidos. Ele encontrará apenas um máximo local)
  2. Pode haver alterações sutis no comportamento se você tiver algumas macros redefinidas de maneira diferente, dependendo de alguns #ifdefs. Penso que estes são casos muito raros, e os testes de unidade que fazem parte da compilação devem captar essas alterações.
Gilad Naor
fonte
1
Tenha cuidado com isso - digamos, existem dois arquivos de cabeçalho que incluem uma definição de algo. Você pode remover um, mas não ambos. Você precisará ser um pouco mais completo em sua abordagem de força bruta.
Dominic Rodger
Talvez seja isso que você quis dizer, mas um script que remove uma única inclusão e deixa a última inclusão removida, se foi removido com êxito, seria suficiente.
Dominic Rodger
1
Péssima ideia. Se um arquivo de cabeçalho # define um BLAH constante e outro arquivo de cabeçalho verifica #ifdef BLAH, a remoção do primeiro arquivo de cabeçalho ainda pode ser compilada com êxito, mas seu comportamento foi alterado.
Graeme Perrow
1
Isso também pode causar problemas com os cabeçalhos do sistema, pois diferentes implementações podem ter itens diferentes incluídos no #include <vector>. Mesmo se você se ater a um compilador, os cabeçalhos podem mudar em versões diferentes.
David Thornley
2
Isso não encontrará casos em que você está incluindo um cabeçalho que inclui o cabeçalho que realmente precisa.
09/15
5

Desculpe (re) postar aqui, as pessoas geralmente não expandem os comentários.

Verifique meu comentário para crashmstr, o FlexeLint / PC-Lint fará isso por você. Mensagem informativa 766. A seção 11.8.1 do meu manual (versão 8.0) discute isso.

Além disso, e isso é importante, continue iterando até que a mensagem desapareça . Em outras palavras, após remover os cabeçalhos não utilizados, reexecutar o fiapo, mais arquivos de cabeçalho podem se tornar "desnecessários" depois de remover alguns cabeçalhos desnecessários. (Isso pode parecer bobagem, leia-o devagar e analise-o, faz sentido.)

Dan
fonte
Eu sei exatamente o que você quer dizer, e minha reação foi "Ewwww". Eu odeio código assim.
David Thornley
5

Eu nunca encontrei uma ferramenta completa que realize o que você está pedindo. A coisa mais próxima que usei é o IncludeManager , que representa graficamente sua árvore de inclusão de cabeçalho, para que você possa visualizar visualmente coisas como cabeçalhos incluídos em apenas um arquivo e inclusões circulares de cabeçalho.

Dan Olson
fonte
4

Eu tentei usar o Flexelint (a versão unix do PC-Lint) e tive resultados um tanto mistos. Provavelmente porque estou trabalhando em uma base de código muito grande e complicada. Eu recomendo examinar cuidadosamente cada arquivo relatado como não utilizado.

A principal preocupação são os falsos positivos. Várias inclusões do mesmo cabeçalho são relatadas como um cabeçalho desnecessário. Isso é ruim, pois o Flexelint não informa em que linha o cabeçalho está incluído ou onde foi incluído antes.

Uma das maneiras pelas quais as ferramentas automatizadas podem errar:

Em A.hpp:

class A { 
  // ...
};

Em B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

Em C.cpp:

#include "C.hpp"  

#include "B.hpp"  // <-- Unneeded, but lint reports it as needed
#include "A.hpp"  // <-- Needed, but lint reports it as unneeded

Se você seguir cegamente as mensagens do Flexelint, irá estragar suas #includes dependências. Existem mais casos patológicos, mas basicamente você precisará inspecionar os cabeçalhos para obter melhores resultados.

Eu recomendo este artigo sobre Estrutura Física e C ++ do blog Games de dentro. Eles recomendam uma abordagem abrangente para limpar a bagunça #include:

Diretrizes

Aqui está um conjunto destilado de diretrizes do livro de Lakos que minimizam o número de dependências físicas entre arquivos. Eu os uso há anos e sempre fiquei muito feliz com os resultados.

  1. Todo arquivo cpp inclui primeiro seu próprio arquivo de cabeçalho. [recorte]
  2. Um arquivo de cabeçalho deve incluir todos os arquivos de cabeçalho necessários para analisá-lo. [recorte]
  3. Um arquivo de cabeçalho deve ter o número mínimo necessário de arquivos de cabeçalho necessários para analisá-lo. [recorte]
Ben Martin
fonte
O livro de Lakos é ótimo para a educação - além de suas observações desatualizadas sobre a tecnologia de compiladores.
Tom
4

Se você estiver usando o Eclipse CDT, tente o http://includator.com, que é gratuito para testadores beta (no momento em que este artigo foi escrito) e remove automaticamente #includes supérfluos ou adiciona os ausentes. Para os usuários que possuem o FlexeLint ou PC-Lint e estão usando o Elicpse CDT, http://linticator.com pode ser uma opção (também gratuita para o teste beta). Enquanto usa a análise de Lint, fornece correções rápidas para remover automaticamente as instruções #include supérfluas.

PeterSom
fonte
O motivo disso é que nosso departamento de contabilidade não é capaz de faturar valores menores. Se você contar o tempo que poderá economizar, não é tão irracional. Uma vez que temos a capacidade de receber pagamentos com cartão de crédito, podemos reduzir significativamente o preço. Outra opção seria um patrocinador para nossos esforços de desenvolvimento. Nosso modelo de financiamento exige que obtenhamos lucros para financiar nosso trabalho de pesquisa. Eu ficaria feliz em vender licenças muito mais baratas, mas não posso. Pode ser que vamos contribuir com a CDT e você a obtém de graça, mas que eu tenho que financiar de alguma forma. Eu esqueci, você pode tentar de graça!
PeterSom
2

Este artigo explica uma técnica de #include remover usando a análise de Doxygen. Esse é apenas um script perl, por isso é bastante fácil de usar.

Steve Gury
fonte
1
O script encontra algumas inclusões para remover, mas também fornece muitas inclusões que não podem ser removidas. Parece que ele não suporta enum de classe, parece também que tem um mau momento com macro e, às vezes, com espaço para nome.
Baptiste Wicht 30/10/12
1

Existe uma ferramenta gratuita que inclui o File Dependencies Watcher, que pode ser integrado no visual studio. Mostra # supérfluo em vermelho.

Vladimir
fonte
1

Existem dois tipos de arquivos #include supérfluos:

  1. Um arquivo de cabeçalho realmente não é necessário para o módulo (.c, .cpp)
  2. Um arquivo de cabeçalho é necessário pelo módulo, mas está sendo incluído mais de uma vez, direta ou indiretamente.

Há duas maneiras na minha experiência que funcionam bem para detectá-lo:

  • gcc -H ou cl.exe / showincludes (resolva o problema 2)

    No mundo real, você pode exportar CFLAGS = -H antes do make, se todos os Makefile não substituírem as opções CFLAGS. Ou, como eu usei, você pode criar um wrapper cc / g ++ para adicionar opções -H à força a cada chamada de $ (CC) e $ (CXX). e inclua o diretório do wrapper na variável $ PATH, então seu make utilizará o comando wrapper. Obviamente, seu wrapper deve chamar o compilador gcc real. Esses truques precisam ser alterados se o Makefile usar o gcc diretamente. em vez de $ (CC) ou $ (CXX) ou por regras implícitas.

    Você também pode compilar um único arquivo ajustando a linha de comando. Mas se você deseja limpar os cabeçalhos de todo o projeto. Você pode capturar toda a saída:

    fazer limpo

    faça 2> & 1 | tee result.txt

  • PC-Lint / FlexeLint (resolva o problema 1 e 2)

    certifique-se de adicionar as opções + e766, este aviso é sobre: ​​arquivos de cabeçalho não utilizados.

    pclint / flint -vf ...

    Isso fará com que os arquivos de cabeçalho incluídos na saída pclint, os arquivos de cabeçalho aninhados sejam recuados adequadamente.

zhaorufei
fonte
1

Para encerrar esta discussão: o pré-processador c ++ está completo. É uma propriedade semântica, se uma inclusão é supérflua. Portanto, segue-se do teorema de Rice que é indecidível se uma inclusão é supérflua ou não. Não pode haver um programa que (sempre corretamente) detecte se uma inclusão é supérflua.

Algoman
fonte
5
Eu pedi uma solução "sempre correta"? Esta resposta não é muito produtiva para a discussão.
precisa
1
Bem, houve vários posts discutindo problemas com os quais esse programa teria que lidar. Meu post fornece uma resposta conclusiva e correta a essa parte da discussão. E eu não gostaria que, se um programa me dissesse, eu pudesse remover com segurança um #include e meu código não fosse mais compilado. (ou pior - ainda compila, mas faz algo diferente). QUALQUER programa tem esse risco.
Algoman
4
Entre toda a especulação sobre quão difícil isso seria e como você PODE resolver um obstáculo ou outro, eu lhe dei a única resposta 100% correta. Acho que é bastante imprudente dizer que este não foi produtivo ...
Algoman
1
Lembrei que o teorema de Rice afirma: "Não pode haver um programa que sempre possa verificar se um determinado programa resolve esse problema de inclusão supérflua". Pode haver alguns programas que resolvem o problema de inclusão supérflua.
Zhe Yang 13/01
1
pessoalmente, achei a entrada do @ Algoman muito útil. me faz perceber o quão difícil é esse problema.
#
0

O PC Lint da Gimpel Software pode relatar quando um arquivo de inclusão foi incluído mais de uma vez em uma unidade de compilação , mas não consegue encontrar arquivos de inclusão que não são necessários na maneira como você está procurando.

Edit: Pode. Veja a resposta de itsmatt

crashmstr
fonte
Você tem certeza disso? Eu não uso o FlexeLint (o mesmo que o PCL) há alguns anos no código C ++, mas mesmo recentemente no código C, eu poderia jurar que vi algumas mensagens (acho que é o código 766?) Sobre arquivos de cabeçalho não utilizados. Apenas verificado (v8.0), consulte a seção 11.8.1. do manual.
Dan