Domanda

Sto lavorando su un grande progetto C++ in Visual Studio 2008 e ci sono molti file con file non necessari #include direttive.A volte il #includes sono solo artefatti e tutto verrà compilato correttamente rimuovendoli, e in altri casi le classi potrebbero essere dichiarate in avanti e #include potrebbe essere spostato nella .cpp file.Esistono buoni strumenti per rilevare entrambi questi casi?

È stato utile?

Soluzione

Anche se non rivelerà i file di inclusione non necessari, Visual Studio ha un'impostazione /showIncludes (cliccare con il tasto destro su a .cpp file, Properties->C/C++->Advanced) che genererà un albero di tutti i file inclusi in fase di compilazione.Ciò può aiutare a identificare i file che non dovrebbero essere inclusi.

Puoi anche dare un'occhiata all'idioma pimpl per farla franca con meno dipendenze dai file di intestazione per rendere più facile vedere le scorie che puoi rimuovere.

Altri suggerimenti

Lanugine del PC funziona abbastanza bene per questo, e trova anche tutti i tipi di altri problemi sciocchi per te.Dispone di opzioni della riga di comando che possono essere utilizzate per creare strumenti esterni in Visual Studio, ma ho scoperto che il file Lanugine visiva addin è più facile da usare.Anche la versione gratuita di Visual Lint aiuta.Ma dai una possibilità a PC-Lint.Configurarlo in modo che non dia troppi avvisi richiede un po' di tempo, ma rimarrai stupito da ciò che apparirà.

C'è un nuovo strumento basato su Clang, includi-ciò-che-usi, che ha lo scopo di farlo.

!!DISCLAIMER!!Lavoro su uno strumento di analisi statica commerciale (non PC Lint).!!DISCLAIMER!!

Esistono diversi problemi con un semplice approccio senza analisi:

1) Set di sovraccarico:

È possibile che una funzione sovraccaricata abbia dichiarazioni che provengono da file diversi.Potrebbe darsi che la rimozione di un file di intestazione comporti la scelta di un sovraccarico diverso anziché un errore di compilazione!Il risultato sarà un cambiamento silenzioso nella semantica che potrebbe essere molto difficile da rintracciare in seguito.

2) Specializzazioni dei modelli:

Similmente all'esempio di sovraccarico, se disponi di specializzazioni parziali o esplicite per un modello, desideri che siano tutte visibili quando viene utilizzato il modello.È possibile che le specializzazioni per il modello primario si trovino in file di intestazione diversi.La rimozione dell'intestazione con la specializzazione non causerà un errore di compilazione, ma potrebbe comportare un comportamento indefinito se quella specializzazione fosse stata selezionata.(Vedere: Visibilità della specializzazione del modello della funzione C++)

Come sottolineato da "msalters", l'esecuzione di un'analisi completa del codice consente anche l'analisi dell'utilizzo della classe.Controllando come viene utilizzata una classe attraverso uno specifico percorso di file, è possibile che la definizione della classe (e quindi tutte le sue dipendenze) possa essere rimossa completamente o almeno spostata ad un livello più vicino alla fonte principale nell'include albero.

Non conosco nessuno di questi strumenti e ho pensato di scriverne uno in passato, ma risulta che questo è un problema difficile da risolvere.

Supponiamo che il tuo file sorgente includa a.h e b.h;a.h contiene #define USE_FEATURE_X e b.h usa #ifdef USE_FEATURE_X.Se #include "a.h" è commentato, il tuo file potrebbe comunque essere compilato, ma potrebbe non fare quello che ti aspetti.Rilevamento di questo programmaticamente non è banale.

Qualunque strumento faccia questo, dovrebbe conoscere anche il tuo ambiente di costruzione.Se a.h assomiglia a:

#if defined( WINNT )
   #define USE_FEATURE_X
#endif

Poi USE_FEATURE_X è definito solo se WINNT è definito, quindi lo strumento dovrebbe sapere quali direttive sono generate dal compilatore stesso e quali sono specificate nel comando di compilazione anziché in un file di intestazione.

Come Timmermans, non ho familiarità con nessuno strumento per questo.Ma ho conosciuto programmatori che hanno scritto uno script Perl (o Python) per provare a commentare ciascuna riga di inclusione una alla volta e quindi compilare ciascun file.


Sembra che ora Eric Raymond ha uno strumento per questo.

Quello di Google cpplint.py ha una regola "includi ciò che usi" (tra molte altre), ma per quanto ne so, no "includi soltanto quello che usi." Anche così, può essere utile.

Se sei interessato a questo argomento in generale, potresti dare un'occhiata a Lakos Progettazione di software C++ su larga scala.È un po' datato, ma affronta molti problemi di "progettazione fisica", come trovare il minimo assoluto di intestazioni che devono essere incluse.Non ho mai visto questo genere di cose discusse da nessun'altra parte.

Dare Includi gestore un tentativo.Si integra facilmente in Visual Studio e visualizza i percorsi di inclusione che ti aiutano a trovare cose non necessarie.Internamente utilizza Graphviz ma ci sono molte altre funzionalità interessanti.E nonostante sia un prodotto commerciale ha un prezzo molto basso.

Puoi creare un grafico di inclusione utilizzando C/C++ include il watcher delle dipendenze dei file, e trova visivamente gli include non necessari.

Se i tuoi file di intestazione generalmente iniziano con

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

(invece di usare #pragma una volta) potresti cambiarlo in:

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

E poiché il compilatore restituisce il nome del file cpp in fase di compilazione, ciò ti consentirebbe di sapere almeno quale file cpp sta causando l'inserimento dell'intestazione più volte.

PC-Lint può effettivamente farlo.Un modo semplice per farlo è configurarlo per rilevare solo i file include non utilizzati e ignorare tutti gli altri problemi.Questo è piuttosto semplice: per abilitare solo il messaggio 766 ("File di intestazione non utilizzato nel modulo"), è sufficiente includere le opzioni -w0 +e766 sulla riga di comando.

Lo stesso approccio può essere utilizzato anche con messaggi correlati come 964 ("File di intestazione non utilizzato direttamente nel modulo") e 966 ("File di intestazione incluso indirettamente non utilizzato nel modulo").

FWIW ne ho parlato in modo più dettagliato in un post sul blog la scorsa settimana su http://www.riverblade.co.uk/blog.php?archive=2008_09_01_archive.xml#3575027665614976318.

Se stai cercando di rimuovere file inutili #include file al fine di ridurre i tempi di compilazione, è possibile spendere meglio tempo e denaro parallelizzando il processo di compilazione utilizzando cl.exe /MP, fare -j, Xoreax IncrediBuild, distcc/gelato, eccetera.

Naturalmente, se hai già un processo di creazione parallela e stai ancora cercando di accelerarlo, allora ripulisci sicuramente il tuo #include direttive e rimuovere quelle dipendenze non necessarie.

Inizia con ciascun file di inclusione e assicurati che ciascun file di inclusione includa solo ciò che è necessario per compilarsi.Qualsiasi file di inclusione mancante per i file C++ può essere aggiunto ai file C++ stessi.

Per ogni file di inclusione e di origine, commenta ciascun file di inclusione uno alla volta e verifica se viene compilato.

È anche una buona idea ordinare i file include in ordine alfabetico e, laddove ciò non sia possibile, aggiungere un commento.

L'aggiunta di una o entrambe le seguenti #Defines escluderà spesso file di intestazione inutili e potrebbe migliorare sostanzialmente i tempi di compilazione, specialmente se il codice che non utilizza le funzioni API di Windows.

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN

Vedere http://support.microsoft.com/kb/166474

Se non lo hai già fatto, utilizzare un'intestazione precompilata per includere tutto ciò che non modificherai (intestazioni della piattaforma, intestazioni SDK esterne o parti statiche già completate del tuo progetto) farà un'enorme differenza nei tempi di creazione.

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

Inoltre, anche se potrebbe essere troppo tardi per il tuo progetto, organizzare il progetto in sezioni e non raggruppare tutte le intestazioni locali in un'unica grande intestazione principale è una buona pratica, anche se richiede un po' di lavoro extra.

Se volessi lavorare con Eclipse CDT potresti provare http://includetor.com per ottimizzare la struttura di inclusione.Tuttavia, Includator potrebbe non sapere abbastanza sugli include predefiniti di VC++ e la configurazione di CDT per utilizzare VC++ con include corretti non è ancora integrata in CDT.

L'ultimo IDE Jetbrains, CLion, mostra automaticamente (in grigio) gli include che non vengono utilizzati nel file corrente.

È anche possibile avere dall'IDE l'elenco di tutti gli include non utilizzati (ma anche funzioni, metodi, ecc...).

Alcune delle risposte esistenti affermano che è difficile.Questo è effettivamente vero, perché è necessario un compilatore completo per individuare i casi in cui una dichiarazione anticipata sarebbe appropriata.Non puoi analizzare C++ senza sapere cosa significano i simboli;la grammatica è semplicemente troppo ambigua per questo.È necessario sapere se un certo nome nomina una classe (potrebbe essere dichiarato in avanti) o una variabile (non può).Inoltre, è necessario essere consapevoli dello spazio dei nomi.

Forse un po' tardi, ma una volta ho trovato uno script Perl WebKit che faceva proprio quello che volevi.Credo che avrà bisogno di qualche adattamento (non sono molto esperto di Perl), ma dovrebbe funzionare:

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)

Se c'è un'intestazione particolare che ritieni non sia più necessaria (dì string.h), puoi commentare che include quindi mettilo sotto tutte le include:

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

Ovviamente le tue intestazioni di interfaccia potrebbero utilizzare una diversa convenzione #define per registrare la loro inclusione nella memoria CPP.O nessuna convenzione, nel qual caso questo approccio non funzionerà.

Quindi ricostruire.Ci sono tre possibilità:

  • Si costruisce bene.String.h non era compilato-critico e l'inclusione può essere rimosso.

  • L'#errore scatta.String.g è stato incluso indirettamente in qualche modo non si sa ancora se String.h è richiesto.Se è necessario, dovresti dire direttamente #includerlo (vedi sotto).

  • Ottieni qualche altro errore di compilazione.String.h era necessario e non è incluso indirettamente, quindi l'inclusione era corretta all'inizio.

Si noti che a seconda dell'inclusione indiretta quando il tuo .h o .c usa direttamente un altro .h è quasi certamente un bug:In effetti stai promettendo che il tuo codice richiederà solo quell'intestazione fintanto che qualche altra intestazione che stai usando lo richiede, il che probabilmente non è quello che intendevi.

Le avvertenze menzionate in altre risposte sulle intestazioni che modificano il comportamento piuttosto che dichiarare cose che causano anche fallimenti di costruzione qui.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top