Rilevamento di #inclusi in C / C ++?


289

Trovo spesso che la sezione delle intestazioni di un file diventi sempre più grande ma non diventa mai più piccola. Nel corso della vita di un file sorgente le classi potrebbero essere state spostate e modificate ed è molto probabile che ce ne siano alcune #includesche non devono più essere presenti. Lasciandoli lì solo prolunga il tempo di compilazione e aggiunge inutili dipendenze di compilazione. Cercare di capire quali sono ancora necessari può essere piuttosto noioso.

Esiste un tipo di strumento in grado di rilevare le direttive #include superflue e suggerire quali posso rimuovere in modo sicuro?
La lanugine lo fa forse?



1
La domanda collegata sembra risolvere il problema solo su Windows, utilizzando Visual Studio in particolare.
D'Nabre,

7
Votando per riaprirlo, poiché il duplicato riguarda in particolare Visual Studio.
Ha disegnato Dormann

Risposte:


42

Non è automatico, ma doxygen produrrà diagrammi di dipendenza per i #includedfile. Dovrai esaminarli visivamente, ma possono essere molto utili per ottenere un'immagine di ciò che sta usando cosa.


5
Questo è un ottimo modo per vedere le catene .. vedere A -> B -> C -> D e A -> D rivela immediatamente la ridondanza.
Tom

34
@ Tom: questa è un'idea orribile: per uno Non mostra se tali inclusioni sono necessarie o meno e in secondo luogo, l'elenco delle inclusioni non dovrebbe dipendere da inclusioni indirette che potrebbero cambiare in futuro (le inclusioni ridondanti di solito non sono tali grosso problema comunque, grazie alle guardie e alla magia del compilatore), ma su quali classi / funzioni sono effettivamente utilizzate nel file (il compilatore non dovrebbe passare attraverso migliaia di righe di codice modello che non vengono nemmeno istanziate)
MikeMB,

@Albert, puoi includere schermate di questo e descrivere brevemente dove fare clic nell'output di doxygen?
Gabriel Staples

@GabrielStaples Non è la mia risposta, quindi non voglio aggiungere informazioni ad esso. Ho corretto solo il collegamento (come luogo di hosting a cui si riferiva fermato / sequestrato per essere utilizzato).
albert

177

Il cppclean di Google (link a: download , documentazione ) può trovare diverse categorie di problemi C ++ e ora può trovare #inclusi superflui.

C'è anche uno strumento basato su Clang, include quello che usi , che può farlo. include-what-you-use può anche suggerire dichiarazioni in avanti (quindi non è necessario #includere così tanto) e facoltativamente ripulire i tuoi #include per te.

Le versioni attuali di Eclipse CDT hanno anche questa funzionalità integrata: andando nel menu Sorgente e facendo clic su Organizza Includi in ordine alfabetico le tue # include, aggiungi eventuali intestazioni che Eclipse pensa di utilizzare senza includerle direttamente e commenta le intestazioni che non ha non credo che tu abbia bisogno. Questa funzionalità non è affidabile al 100%, tuttavia.


2
Lo fa adesso. Ho appena iniziato a usarlo. Vedi la mia nota qui. stackoverflow.com/questions/1301850/…
Chance

1
Il repository cppclean è inattivo, ora puoi scaricarlo qui: bitbucket.org/robertmassaioli/cppclean (il sito originale è comunque utile per un uso esemplificativo)
Nick

3
Ho aggiornato il collegamento a un fork cppclean mantenuto: github.com/myint/cppclean
BenC

1
Si noti che cppclean sembra trovarli solo nei file di intestazione, non nei file cpp, dal documento: "# inutili # include nei file di intestazione".
Zitrax,

1
@wizurd - Non ho tenuto il passo con i recenti sviluppi di Eclipse CDT, ma non la penso così. iwyu è completo e relativamente lento. L'analisi di Eclipse CDT è veloce (interattiva) e, quando l'ho testata, meno accurata.
Josh Kelley,

65

Guarda anche include-what-you-use , che risolve un problema simile.


6
IMHO questa risposta richiede molti più voti, dato che una volta elaborati i nodi, lo strumento IWYU di Google sarà lo strumento definitivo per questa attività.
Dan Olson,

5
sudo apt-get install iwyu
Andrew Wagner,

Sembra fantastico - con due cavaets 1) ultimo aggiornamento 2106 febbraio 2) I Gogole stessi lo usano solo per C ++, non per C, che l'OP ha richiesto.
Mawg dice di ripristinare Monica il

Puoi spiegarci un po 'come un utente dovrebbe usarlo? Il file README non è molto chiaro su cosa contenga l'output dello script python.
Giullare del re,

Sto usando questo, ma non è sempre corretto al 100%. Forse il 70% delle volte dà i suggerimenti corretti.
InQusitive,

25

Il problema con il rilevamento di inclusioni superflue è che non può essere solo un controllo di dipendenza del tipo. Un'inclusione superflua è un file che non fornisce alcun valore alla compilazione e non modifica un altro elemento da cui dipendono altri file. Esistono molti modi in cui un file di intestazione può modificare una compilazione, ad esempio definendo una costante, ridefinendo e / o eliminando una macro utilizzata, aggiungendo uno spazio dei nomi che altera la ricerca di un nome in qualche modo lungo la riga. Per rilevare elementi come lo spazio dei nomi è necessario molto più di un preprocessore, in realtà è quasi necessario un compilatore completo.

Lint è più un controllo di stile e sicuramente non avrà questa piena capacità.

Penso che troverai l'unico modo per rilevare un'inclusione superflua è rimuovere, compilare ed eseguire le suite.


8
Niente di tutto questo sarà un problema se i file include sono disposti correttamente. Se hai mai bisogno di includere il file A prima del file B, stai sbagliando (e ho lavorato su progetti in cui hanno sbagliato).
David Thornley,

9
@ David, sì, ma dipende dagli anni degli sviluppatori prima di farlo correttamente. Posso dire con grande certezza che le probabilità che ciò accada favoriscono la casa, non tu :(
JaredPar

Sì, ma generalmente lo scopro quando modifico un programma e improvvisamente ho un errore di compilazione (se sono fortunato) o un bug oscuro. Ciò sembra mantenere onesti i file #include, almeno a lungo termine.
David Thornley,

Direi il contrario. Tutto ciò che serve è un controllo delle dipendenze di tipo. Potrebbe non essere compilato dopo aver organizzato le inclusioni di conseguenza, ma questi sono problemi che dovrebbero essere risolti comunque.
Benoît,

1
@Benoit, allora ignoreresti una classe di problemi che si compilano ma cambiano semanticamente il significato del tuo programma. Considera come un #define in un file può modificare un ramo #if in un altro. La rimozione di un'intestazione può comunque consentire la compilazione di questo con risultati diversi
JaredPar

15

Pensavo che PCLint avrebbe fatto questo, ma sono passati alcuni anni da quando l'ho visto. Potresti verificarlo.

Ho guardato questo blog e l'autore ha parlato un po 'della configurazione di PCLint per trovare inclusioni non utilizzate. Potrebbe meritare un'occhiata.


Buona scoperta! Dovrò usarlo.
crashmstr

4
Uso PCLint regolarmente e mi dice di intestazioni inutilizzate. Sto attento a commentare l'intestazione #include e ricompilare per essere sicuro che l'intestazione sia veramente inutilizzata ...
Harold Bamford

Grazie per la conferma, Harold.
itsmatt

5
troppo caro. non uno strumento praticabile per le masse.

7

Il browser di refactoring di CScout è in grado di rilevare direttive di inclusione superflue nel codice C (purtroppo non C ++). Puoi trovare una descrizione di come funziona in questo articolo di giornale.


5

È possibile scrivere uno script rapido che cancella una singola direttiva #include, compila i progetti e registra il nome in #include e il file da cui è stato rimosso nel caso in cui non si siano verificati errori di compilazione.

Lascialo funzionare durante la notte e il giorno successivo avrai un elenco corretto al 100% di file include che puoi rimuovere.

A volte la forza bruta funziona :-)


modifica: e a volte no :-). Ecco alcune informazioni dai commenti:

  1. A volte è possibile rimuovere due file di intestazione separatamente, ma non entrambi insieme. Una soluzione è rimuovere i file di intestazione durante l'esecuzione e non ripristinarli. Questo troverà un elenco di file che puoi rimuovere in modo sicuro, anche se potrebbe esserci una soluzione con più file da rimuovere che questo algoritmo non troverà. (è una ricerca avida nello spazio dei file di inclusione da rimuovere. Troverà solo un massimo locale)
  2. Potrebbero esserci sottili cambiamenti nel comportamento se alcune macro sono state ridefinite in modo diverso a seconda di alcuni #ifdefs. Penso che si tratti di casi molto rari e i test unitari che fanno parte della build dovrebbero rilevare questi cambiamenti.

1
Fai attenzione: supponi che ci siano due file di intestazione che includono entrambi una definizione di qualcosa. Puoi rimuovere entrambi, ma non entrambi. Dovrai essere un po 'più approfondito nel tuo approccio alla forza bruta.
Dominic Rodger,

Forse questo è ciò che intendevi, ma uno script che rimuove una singola inclusione e lascia fuori l'ultima inclusione rimossa se fosse stato rimosso con successo avrebbe funzionato.
Dominic Rodger,

1
Cattiva idea. Se un file di intestazione # definisce un BLAH costante e un altro file di intestazione verifica #ifdef BLAH, la rimozione del primo file di intestazione potrebbe comunque essere compilata correttamente ma il comportamento è cambiato.
Graeme Perrow,

1
Ciò può anche causare problemi con le intestazioni di sistema, poiché diverse implementazioni potrebbero includere elementi diversi in #include <vector>. Anche se ti attieni a un compilatore, le intestazioni potrebbero cambiare su versioni diverse.
David Thornley,

2
Questo non troverà casi in cui includi un'intestazione che include l'intestazione di cui hai veramente bisogno.
bk1e,

5

Siamo spiacenti di (ri) pubblicare qui, le persone spesso non espandono i commenti.

Controlla il mio commento a crashmstr, FlexeLint / PC-Lint farà questo per te. Messaggio informativo 766. La sezione 11.8.1 del mio manuale (versione 8.0) ne discute.

Inoltre, e questo è importante, continua a ripetere fino a quando il messaggio non scompare . In altre parole, dopo aver rimosso le intestazioni non utilizzate, rieseguire lint, un numero maggiore di file di intestazione potrebbe essere diventato "non necessario" dopo aver rimosso alcune intestazioni non necessarie. (Potrebbe sembrare sciocco, leggerlo lentamente e analizzarlo, ha senso.)


So esattamente cosa intendi, e la mia reazione è stata "Ewwww". Odio il codice in quel modo.
David Thornley,

5

Non ho mai trovato uno strumento completo che realizza ciò che stai chiedendo. La cosa più vicina che ho usato è IncludeManager , che rappresenta graficamente l'albero di inclusione dell'intestazione in modo da poter individuare visivamente cose come le intestazioni incluse in un solo file e le inclusioni circolari dell'intestazione.


4

Ho provato ad usare Flexelint (la versione unix di PC-Lint) e ho ottenuto risultati piuttosto contrastanti. Questo è probabilmente perché sto lavorando su una base di codice molto grande e intricata. Consiglio di esaminare attentamente ogni file segnalato come inutilizzato.

La preoccupazione principale sono i falsi positivi. Molteplici inclusioni della stessa intestazione vengono riportate come intestazione non necessaria. Ciò è negativo poiché Flexelint non ti dice su quale riga è inclusa l'intestazione o dove è stata inclusa in precedenza.

Uno dei modi in cui gli strumenti automatici possono sbagliare:

In A.hpp:

class A { 
  // ...
};

In B.hpp:

#include "A.hpp

class B {
    public:
        A foo;
};

In 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 segui ciecamente i messaggi di Flexelint, confonderai le tue dipendenze #include. Ci sono più casi patologici, ma fondamentalmente dovrai ispezionare tu stesso le intestazioni per ottenere i migliori risultati.

Consiglio vivamente questo articolo su Physical Structure e C ++ dal blog Games dall'interno. Raccomandano un approccio globale per ripulire il disordine #include:

Linee guida

Ecco una serie distillata di linee guida dal libro di Lakos che minimizzano il numero di dipendenze fisiche tra i file. Li uso da anni e sono sempre stato molto contento dei risultati.

  1. Ogni file cpp include prima il proprio file di intestazione. [Omissis]
  2. Un file di intestazione deve includere tutti i file di intestazione necessari per analizzarlo. [Omissis]
  3. Un file di intestazione deve avere il numero minimo di file di intestazione necessario per analizzarlo. [Omissis]

Il libro di Lakos è ottimo per l'educazione - a parte le sue osservazioni obsolete sulla tecnologia dei compilatori.
Tom

4

Se stai usando Eclipse CDT puoi provare http://includator.com che è gratuito per i beta tester (al momento in cui scrivo) e rimuove automaticamente #includi superflui o aggiunge quelli mancanti. Per quegli utenti che hanno FlexeLint o PC-Lint e usano Elicpse CDT, http://linticator.com potrebbe essere un'opzione (anche gratuita per il beta test). Mentre utilizza l'analisi di Lint, fornisce soluzioni rapide per rimuovere automaticamente le istruzioni #include superflue.


Il motivo è che il nostro dipartimento di contabilità non è in grado di fatturare importi inferiori. Se conti il ​​tempo che potresti risparmiare non è così irragionevole. Una volta, abbiamo la possibilità di ottenere pagamenti con carta di credito, possiamo abbassare significativamente il prezzo. Un'altra opzione sarebbe uno sponsor per i nostri sforzi di sviluppo. Il nostro modello di finanziamento ci richiede di guadagnare profitti per finanziare il nostro lavoro di ricerca. Sarei felice di vendere licenze molto più economiche, ma non posso. Forse contribuiremo a CDT e tu lo riceverai gratuitamente, ma che devo finanziare in qualche modo. Ho dimenticato, puoi provare gratuitamente!
PeterSom,

2

Questo articolo spiega una tecnica di rimozione di #include usando l'analisi di Doxygen. Questo è solo uno script perl, quindi è abbastanza facile da usare.


1
Lo script trova alcune inclusioni da rimuovere ma fornisce anche molte inclusioni che non possono essere rimosse. Sembra che non supporti l'enum di classe, sembra anche che abbia un brutto momento con macro e talvolta con spazio dei nomi.
Baptiste Wicht,



1

Esistono due tipi di file #include superflui:

  1. Un file di intestazione in realtà non è necessario per il modulo (.c, .cpp)
  2. Il modulo richiede un file di intestazione, ma viene incluso più di una volta, direttamente o indirettamente.

Esistono 2 modi nella mia esperienza che funziona bene per rilevarlo:

  • gcc -H o cl.exe / showincludes (risolvi il problema 2)

    Nel mondo reale, puoi esportare CFLAGS = -H prima di make, se tutti i Makefile non hanno la precedenza sulle opzioni CFLAGS. O come ho usato, puoi creare un wrapper cc / g ++ per aggiungere forzatamente le opzioni -H a ogni invocazione di $ (CC) e $ (CXX). e anteporre la directory del wrapper alla variabile $ PATH, quindi make farà invece uso del comando wrapper. Ovviamente il tuo wrapper dovrebbe invocare il vero compilatore gcc. Questi trucchi devono cambiare se il tuo Makefile usa direttamente gcc. invece di $ (CC) o $ (CXX) o secondo regole implicite.

    Puoi anche compilare un singolo file modificando la riga di comando. Ma se vuoi pulire le intestazioni per l'intero progetto. È possibile acquisire tutto l'output:

    pulire

    fare 2> & 1 | tee result.txt

  • PC-Lint / FlexeLint (risolvi il problema sia 1 che 2)

    assicurati di aggiungere le opzioni + e766, questo avviso riguarda: file di intestazione inutilizzati.

    pclint / flint -vf ...

    Ciò causerà l'output di pclint inclusi i file di intestazione, i file di intestazione nidificati verranno rientrati in modo appropriato.


1

Per concludere questa discussione: il preprocessore c ++ è in fase di completamento. È una proprietà semantica, se un'inclusione è superflua. Quindi, dal teorema di Rice risulta che è indecidibile se una inclusione sia superflua o meno. NON può esserci un programma, che (sempre correttamente) rileva se una inclusione è superflua.


5
Ho chiesto una soluzione "sempre corretta"? Questa risposta non è molto produttiva per la discussione.
shoosh

1
Bene, ci sono stati numerosi post che parlavano di problemi che un simile programma avrebbe dovuto affrontare. Il mio post fornisce una risposta conclusiva e corretta a quella parte della discussione. E io per primo non mi piacerebbe, se un programma me lo avesse detto, avrei potuto tranquillamente rimuovere un #include e quindi il mio codice non si sarebbe più compilato. (o peggio - compila ancora ma fa qualcosa di diverso). QUALSIASI programma di questo tipo comporta questo rischio.
Algoman

4
Tra tutte le SPETTACOLI su quanto sarebbe difficile e su come POTETE risolvere un ostacolo o un altro, vi ho dato l'unica risposta corretta al 100%. Trovo abbastanza impudente dire che questo non era produttivo ...
Algoman

1
Ho ricordato che il teorema di Rice afferma "Non può esserci un programma che possa sempre verificare se un determinato programma risolve questo problema di superfluo-include". Ci possono essere alcuni programmi che risolvono il problema delle inclusioni superflue.
Zhe Yang,

1
personalmente ho trovato molto utile l'input di @Algoman. mi fa capire quanto sia difficile questo problema.
Bogardon,


0

PC Lint di Gimpel Software può riferire quando un file include è stato incluso più di una volta in un'unità di compilazione , ma non riesce a trovare i file include che non sono necessari nel modo che stai cercando.

Modifica: può. Vedi la risposta di itsmatt


Ne sei sicuro ? Non uso FlexeLint (come PCL) da alcuni anni sul codice C ++, ma anche di recente sul codice C, potrei giurare di aver visto un paio di messaggi (penso che sia il codice 766?) Su file di intestazione inutilizzati. Appena verificato (v8.0), vedere la sezione 11.8.1. del manuale.
Dan

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.