Domanda

Eventuali pratiche consigliate per la pulizia di "intestazione spaghetti" che sta causando estremamente tempi di compilazione lenti (Linux / Unix)?

Esiste un valore equivalente a & #; #pragma una volta " con GCC?
(trovato messaggi contrastanti al riguardo)

Grazie.

È stato utile?

Soluzione

Supponendo che tu abbia familiarità con " include guards " (#ifdef all'inizio dell'intestazione ..), un ulteriore modo per velocizzare i tempi di costruzione è usare le protezioni di inclusione esterne. È stato discusso in " Progettazione software C ++ su larga scala " ;. L'idea è che le guardie di inclusione classiche, a differenza di #pragma una volta, non risparmiano l'analisi del preprocessore richiesta per ignorare l'intestazione dalla seconda volta (cioè deve ancora analizzare e cercare l'inizio e la fine della guardia di inclusione. le protezioni di inclusione esterne posizionano gli # ifdef attorno alla stessa riga #include.

Quindi sembra così:

#ifndef MY_HEADER
#include "myheader.h"
#endif

e ovviamente nel file H hai il classico include guard

#ifndef MY_HEADER
#define MY_HEADER

// content of header

#endif

In questo modo il file myheader.h non viene nemmeno aperto / analizzato dal preprocessore e può farti risparmiare molto tempo in progetti di grandi dimensioni, specialmente quando i file di intestazione si trovano su posizioni remote condivise, come a volte fanno.

di nuovo, è tutto in quel libro. hth

Altri suggerimenti

Se vuoi fare una pulizia completa e avere il tempo di farlo, la soluzione migliore è eliminare tutti i #include in tutti i file (tranne quelli ovvi, ad esempio abc.h in abc.cpp) e quindi compilare il progetto. Aggiungi la dichiarazione in avanti o l'intestazione necessarie per correggere il primo errore, quindi ripeti fino a quando non esegui la compilazione in modo pulito.

Questo non risolve i problemi sottostanti che possono comportare problemi di inclusione, ma garantisce che gli unici inclusi siano quelli richiesti.

Ho letto che GCC considera #pragma una volta deprecato, anche se anche #pragma una volta può fare così tanto per accelerare le cose.

Per provare a districare gli spaghetti #include , puoi guardare doxygen . Dovrebbe essere in grado di generare grafici delle intestazioni incluse, che potrebbero darti un vantaggio sulla semplificazione delle cose. Non riesco a ricordare i dettagli con disinvoltura, ma le funzionalità del grafico potrebbero richiedere l'installazione di GraphViz e dire a doxygen il percorso in cui è possibile trovare dotty.exe di GraphViz.

Un altro approccio che potresti prendere in considerazione se il tempo di compilazione è la tua principale preoccupazione è la creazione di Header precompilati .

L'altro giorno ho letto di un trucco per ridurre le dipendenze delle intestazioni: scrivi uno script che

  • trova tutte le dichiarazioni #include
  • rimuove un'istruzione alla volta e ricompila
  • se la compilazione non riesce, aggiungi nuovamente l'istruzione include in

Alla fine, si spera che finirai con il minimo di inclusioni richieste nel tuo codice. È possibile scrivere uno script simile che riorganizza le inclusioni per scoprire se sono autosufficienti o se è necessario includere altre intestazioni prima di esse (includere prima l'intestazione, vedere se la compilazione fallisce, segnalarla). Questo dovrebbe andare in qualche modo a ripulire il tuo codice.

Altre note:

  • I compilatori moderni (tra cui gcc) riconoscono le protezioni delle intestazioni e si ottimizzano come una volta pragma, aprendo il file solo una volta.
  • pragma una volta può essere problematico quando lo stesso file ha nomi diversi nel filesystem (cioè con soft-link)

  • gcc supporta #pragma una volta, ma lo chiama " obsoleto "
  • pragma una volta non è supportato da tutti i compilatori e non fa parte dello standard C

  • non solo i compilatori possono essere problematici. Strumenti come Incredibuild hanno anche problemi con #pragma una volta

Richard aveva ragione (perché la sua soluzione è stata annotata?).

Comunque, tutte le intestazioni C / C ++ dovrebbero usare le protezioni di inclusione interne.

Detto questo:

1 - Il tuo codice legacy non è più realmente gestito e dovresti usare le intestazioni precompilate (che sono un hack, ma hey ... La tua necessità è quella di accelerare la compilazione, non refactificare il codice non mantenuto)

2 - Il tuo codice legacy è ancora attivo. Quindi, puoi utilizzare le intestazioni precompilate e / o le protezioni / protezioni esterne per una soluzione temporanea, ma alla fine dovrai rimuovere tutte le tue inclusioni, una .C o .CPP alla volta, e compilarle. File C o .CPP uno alla volta, correggendo le loro inclusioni con dichiarazioni in avanti o include quando necessario (o anche suddividendo una grande inclusione in più piccole per essere sicuri che ogni file .C o .CPP otterrà solo le intestazioni di cui ha bisogno). Ad ogni modo, testare e rimuovere inclusioni obsolete fa parte della manutenzione di un progetto, quindi ...

La mia esperienza con le intestazioni precompilate non è stata esattamente una buona, perché per la metà del tempo il compilatore non è riuscito a trovare un simbolo che avevo definito, e quindi ho provato un "pulito / ricostruito" completo per essere sicuro non l'intestazione precompilata che era obsoleta. Quindi la mia ipotesi è di usarlo per librerie esterne che non toccherete nemmeno (come STL, intestazioni API C, Boost, qualunque cosa). Tuttavia, la mia esperienza è stata con Visual C ++ 6, quindi immagino (spero?) Che abbiano capito bene, ora.

Ora, un'ultima cosa: le intestazioni dovrebbero essere sempre autosufficienti. Ciò significa che se l'inclusione delle intestazioni dipende dall'ordine di inclusione, allora hai un problema. Ad esempio, se riesci a scrivere:

#include "AAA.hpp"
#include "BBB.hpp"

Ma non:

#include "BBB.hpp"
#include "AAA.hpp"

poiché BBB dipende da AAA, allora tutto ciò che hai è una dipendenza che non hai mai riconosciuto nel codice. Non riconoscerlo con una definizione renderà la tua compilation solo un incubo. Anche BBB dovrebbe includere AAA (anche se potrebbe essere un po 'più lento: alla fine, le dichiarazioni a termine puliranno comunque le inclusioni inutili, quindi dovresti avere un timer di compilazione più veloce).

Usa uno o più di quelli per accelerare il tempo di costruzione

  1. Usa intestazioni precompilate
  2. Utilizza un meccanismo di memorizzazione nella cache (ad esempio scons)
  3. Utilizza un sistema di compilazione distribuito (distcc, Incredibuild ($))

Nelle intestazioni: includi le intestazioni solo se non puoi utilizzare la dichiarazione diretta, ma includi sempre #include qualsiasi file di cui hai bisogno (includere le dipendenze sono cattive!).

Come menzionato nell'altra risposta, dovresti assolutamente usare dichiarazioni anticipate ogni volta che è possibile. Per quanto ne so, GCC non ha niente di equivalente a #pragma una volta, motivo per cui mi attengo al vecchio stile di moda delle guardie inclusive.

Grazie per le risposte, ma la domanda riguarda il codice esistente che include rigoroso "include ordine" eccetera. La domanda è se ci sono strumenti / script per chiarire cosa sta realmente accadendo.

Le protezioni di intestazione non sono la soluzione in quanto non impediscono al compilatore di leggere l'intero file ancora e ancora e ...

PC-Lint farà molto per ripulire le intestazioni degli spaghetti. Inoltre risolverà altri problemi anche per te come variabili non inizializzate che non vengono visualizzate, ecc.

Come commentato onebyone.livejournal.com in una risposta alla tua domanda, alcuni compilatori supportano include l'ottimizzazione della protezione , che la pagina che ho collegato definisce come segue:

  

L'ottimizzazione di include guard è quando un compilatore riconosce l'idioma interno include guard descritto sopra e prende provvedimenti per evitare di aprire il file più volte. Il compilatore può guardare un file di inclusione, eliminare i commenti e gli spazi bianchi e capire se l'intero file è compreso nelle protezioni di inclusione. In tal caso, memorizza il nome file e include le condizioni di guardia in una mappa. Alla successiva richiesta al compilatore di includere il file, è possibile verificare la condizione di inclusione della protezione e decidere se saltare il file o #includerlo senza dover aprire il file.

Quindi, hai già risposto che le protezioni di inclusione esterne non sono la risposta alla tua domanda. Per districare i file di intestazione che devono essere inclusi in un ordine specifico, suggerirei quanto segue:

  • Ogni file .c o .cpp deve prima #include il file .h corrispondente e il file il resto delle direttive #include dovrebbe essere ordinato alfabeticamente. Generalmente si verificano errori di compilazione quando si interrompono dipendenze non dichiarate tra i file di intestazione.
  • Se si dispone di un file di intestazione che definisce i typedef globali per i tipi di base o le direttive globali #define utilizzate per la maggior parte del codice, ogni file .h dovrebbe < code> #include prima quel file, e il resto delle sue direttive #include dovrebbero essere ordinati alfabeticamente.
  • Quando queste modifiche causano errori di compilazione, in genere è necessario aggiungere una dipendenza esplicita da un file di intestazione a un altro, sotto forma di #include .
  • Quando queste modifiche non causano errori di compilazione, potrebbero causare cambiamenti comportamentali. Spero che tu abbia una sorta di suite di test che puoi usare per verificare la funzionalità della tua applicazione.

Sembra anche che parte del problema potrebbe essere che le build incrementali sono molto più lente di quanto dovrebbero essere. Questa situazione può essere migliorata con dichiarazioni a termine o un sistema di compilazione distribuito, come altri hanno sottolineato.

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