C / C ++: rilevamento di #inclusi superflui?

Trovo spesso che la sezione delle intestazioni di un file diventa sempre più grande ma non diventa mai più piccola. Durante la vita di un file sorgente, le classi possono essere state spostate e ridimensionate ed è molto probabile che ci siano parecchi #includes che non hanno bisogno di essere lì e più. Lasciandoli lì si prolunga il tempo di compilazione e si aggiungono dipendenze di compilazione non necessarie. Cercare di capire quali sono ancora necessari può essere piuttosto noioso.

Esiste qualche tipo di strumento in grado di rilevare le direttive #include superflue e suggerire quali posso rimuovere in sicurezza?
Lo fa forse?

    Non è automatico, ma Doxygen genererà diagrammi di dipendenza per i file #inclusi. Dovrai attraversarli visivamente, ma possono essere molto utili per ottenere un’immagine di cosa sta usando cosa.

    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-what-you-use , che può farlo. include-what-you-use può anche suggerire dichiarazioni in avanti (quindi non devi #include così tanto) e opzionalmente ripulire il tuo #include per te.

    Le versioni correnti di Eclipse CDT hanno anche questa funzionalità integrata: andando nel menu Sorgente e facendo clic su Organizza include alfabetizzerà i tuoi # include, aggiungerà eventuali intestazioni che Eclipse pensa che tu stia utilizzando senza includerle direttamente e commenta qualsiasi intestazione che non fa penso che tu abbia bisogno. Questa funzione non è affidabile al 100%, tuttavia.

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

    Il problema con la rilevazione di include superflui è che non può essere solo un controllore di dipendenza di tipo. Un include superfluo è un file che non fornisce nulla di valore per la compilazione e non altera un altro elemento di cui altri file dipendono. Ci sono molti modi in cui un file di intestazione può alterare una compilazione, ad esempio definendo una costante, ridefinendo e / o cancellando una macro usata, aggiungendo uno spazio dei nomi che altera la ricerca di un nome in qualche modo lungo la linea. Per poter rilevare elementi come lo spazio dei nomi hai bisogno di molto più di un preprocessore, in effetti hai quasi bisogno di un compilatore completo.

    Lint è più un controllore di stile e certamente non avrà questa piena capacità.

    Penso che troverete che l’unico modo per rilevare un superfluo include è quello di rimuovere, compilare ed eseguire suite.

    Pensavo che PCLint avrebbe fatto questo, ma sono passati alcuni anni da quando l’ho guardato. Potresti controllare.

    Ho guardato questo blog e l’autore ha parlato un po ‘della configurazione di PCLint per trovare gli include inutilizzati. Potrebbe meritare un’occhiata.

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

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

    Lascia che funzioni durante la notte e il giorno dopo avrai un elenco corretto al 100% di file inclusi che puoi rimuovere.

    A volte la forza bruta funziona solo 🙂


    modifica: a volte non lo fa :-). Ecco un po ‘di informazioni dai commenti:

    1. A volte è ansible rimuovere separatamente due file di intestazione, ma non entrambi insieme. Una soluzione è rimuovere i file di intestazione durante l’esecuzione e non portarli indietro. Questo troverà una lista di file che puoi rimuovere in tutta sicurezza, anche se potrebbe esserci una soluzione con più file da rimuovere che questo algoritmo non troverà. (è una ricerca avida sullo spazio di includere i file da rimuovere. Troverà solo un massimo locale)
    2. Ci possono essere sottili cambiamenti nel comportamento se hai alcune macro ridefinite in modo diverso a seconda di alcuni #ifdefs. Penso che questi siano casi molto rari, e i Test unitari che fanno parte della build dovrebbero prendere questi cambiamenti.

    Mi dispiace (ri) postare qui, le persone spesso non espandono i commenti.

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

    Inoltre, e questo è importante, continua a ripetere fino a quando il messaggio non scompare . In altre parole, dopo aver rimosso le intestazioni inutilizzate, rieseguire lanugine, altri file di intestazione potrebbero essere diventati “non necessari” una volta rimosse alcune intestazioni non necessarie. (Potrebbe sembrare sciocco, leggerlo lentamente e analizzarlo, ha senso).

    Non ho mai trovato uno strumento completo che realizza ciò che stai chiedendo. La cosa più vicina che ho usato è IncludeManager , che traccia il grafico dell’albero di inclusione dell’intestazione in modo che tu possa vedere visivamente cose come intestazioni incluse in un solo file e inclusioni di intestazione circolari.

    Se stai usando Eclipse CDT puoi provare http://includator.com che è gratuito per i beta tester (al momento di questa stesura) e rimuove automaticamente #inclusi superflui o aggiunge quelli mancanti. Per gli utenti che dispongono di FlexeLint o PC-Lint e utilizzano 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 affermazioni #include superflue.

    Ho provato a usare Flexelint (la versione unix di PC-Lint) e ho avuto risultati un po ‘misti. Questo è probabile perché sto lavorando su una base di codice molto ampia e intricata. Consiglio di esaminare attentamente ogni file segnalato come non utilizzato.

    La principale preoccupazione sono i falsi positivi. Più include della stessa intestazione sono riportati come un’intestazione non necessaria. Questo è un problema poiché Flexelint non ti dice quale riga è inclusa nell’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, cancelli le tue dipendenze #include. Ci sono più casi patologici, ma fondamentalmente avrete bisogno di ispezionare personalmente le intestazioni per ottenere i migliori risultati.

    Consiglio vivamente questo articolo su Physical Structure e C ++ dal blog Games from within. Raccomandano un approccio completo per ripulire il pasticcio #include:

    Linee guida

    Ecco una serie distinta di linee guida dal libro di Lakos che minimizzano il numero di dipendenze fisiche tra i file. Li ho usati per anni e sono sempre stato davvero 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 dovrebbe avere il minimo numero di file di intestazione necessari per analizzarlo. [Omissis]

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

    C’è uno strumento gratuito Include Dependencies Watcher che può essere integrato nello studio visivo. Mostra #inclusi superflui in rosso.

    Per concludere questa discussione: il preprocessore c ++ è completo. È una proprietà semantica, se l’inclusione è superflua. Quindi, dal teorema di Rice risulta che è indecidibile se un inclusivo sia superfluo o meno. Non ci può essere un programma, che (sempre correttamente) rileva se una inclusione è superflua.

    Forse un po ‘tardi, ma una volta ho trovato uno script perl WebKit che ha fatto esattamente quello che volevi. Avrò bisogno di un adattamento, credo (non sono esperto di perl), ma dovrebbe fare il trucco:

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

    (questo è un vecchio ramo perché il tronco non ha più il file)

    Esistono due tipi di file #include superflui:

    1. Un file di intestazione non è effettivamente necessario dal modulo (.c, .cpp)
    2. Un modulo di intestazione è necessario dal modulo ma viene incluso più volte, direttamente o indirettamente.

    Ci sono 2 modi nella mia esperienza che funziona bene a rilevarlo:

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

      Nel mondo reale, è ansible esportare CFLAGS = -H prima del make, se tutto il Makefile non ha l’override delle opzioni CFLAGS. O come ho usato, puoi creare un wrapper cc / g ++ per aggiungere le opzioni -H forzatamente ad ogni richiamo di $ (CC) e $ (CXX). e anteponi la directory del wrapper alla variabile $ PATH, quindi il tuo make userà invece il comando wrapper. Ovviamente il tuo wrapper dovrebbe invocare il vero compilatore gcc. Questo trucco deve cambiare se il tuo Makefile usa direttamente gcc. invece di $ (CC) o $ (CXX) o regole implicite.

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

      rendere pulito

      fai 2> & 1 | tee result.txt

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

      assicurati di aggiungere le opzioni + e766, questo avviso riguarda: i file di intestazione non utilizzati.

      pclint / flint -vf …

      Ciò causerà l’inclusione dei file di intestazione inclusi nell’output di pclint, i file di intestazione annidati saranno rientrati in modo appropriato.

    Ecco un semplice metodo di forza bruta per identificare l’intestazione superflua . Non è perfetto ma elimina gli “ovvi” non necessari inclusi. Liberarsi di questi è molto importante per pulire il codice.

    Gli script sono accessibili direttamente su GitHub.

    PC Lint di Gimpel Software può segnalare quando un file di inclusione è stato incluso più di una volta in un’unità di compilazione , ma non riesce a trovare i file di inclusione che non sono necessari nel modo in cui si sta cercando.

    Modifica: può. Vedi la risposta di itsmatt

    CLion , l’IDE C / C ++ di JetBrains, rileva che il ridondante include immediatamente . Sono visualizzati in grigio nell’editor, ma ci sono anche funzioni per l’ ottimizzazione incluse nel file corrente o nell’intero progetto .

    Ho trovato che si paga per questa funzionalità però; CLion impiega un po ‘di tempo a scansionare e analizzare il tuo progetto quando viene caricato per la prima volta.