In quale ordine devono essere specificati i file, ovvero quali sono i motivi per includere un'intestazione prima di un'altra?
Ad esempio, i file di sistema, STL e Boost vanno prima o dopo i file di inclusione locali?
In quale ordine devono essere specificati i file, ovvero quali sono i motivi per includere un'intestazione prima di un'altra?
Ad esempio, i file di sistema, STL e Boost vanno prima o dopo i file di inclusione locali?
Risposte:
Non credo che ci sia un ordine consigliato, purché si compili! La cosa fastidiosa è quando alcune intestazioni richiedono che le altre intestazioni vengano incluse per prime ... Questo è un problema con le intestazioni stesse, non con l'ordine delle inclusioni.
La mia preferenza personale è quella di passare dal locale al globale, ogni sottosezione in ordine alfabetico, ovvero:
La mia logica per 1. è che dovrebbe dimostrare che ogni intestazione (per la quale esiste un cpp) può essere #include
d senza prerequisiti (terminus technicus: l'intestazione è "autonoma"). E il resto sembra fluire logicamente da lì.
La cosa importante da tenere a mente è che le intestazioni non dovrebbero dipendere da altre intestazioni incluse per prime. Un modo per assicurare ciò è includere le intestazioni prima di qualsiasi altra intestazione.
"Thinking in C ++" in particolare menziona questo, riferendosi al "Design software su larga scala C ++" di Lakos:
Gli errori di utilizzo latenti possono essere evitati assicurando che il file .h di un componente analizzi da solo - senza dichiarazioni o definizioni fornite esternamente ... Includendo il file .h come prima riga del file .c si assicura che nessun pezzo critico delle informazioni intrinseche all'interfaccia fisica del componente mancano dal file .h (o, se esiste, che lo scoprirai non appena provi a compilare il file .c).
Vale a dire, includere nel seguente ordine:
Se una delle intestazioni ha un problema con l'inserimento in questo ordine, correggili (se i tuoi) o non utilizzarli. Librerie di boicottaggio che non scrivono intestazioni pulite.
La guida di stile C ++ di Google sostiene quasi il contrario, senza alcuna giustificazione; Personalmente tendo a favorire l'approccio Lakos.
Seguo due semplici regole che evitano la stragrande maggioranza dei problemi:
Seguo anche le linee guida di:
In altre parole:
#include <stdio.h>
#include <string.h>
#include "btree.h"
#include "collect_hash.h"
#include "collect_arraylist.h"
#include "globals.h"
Tuttavia, essendo linee guida, è una cosa soggettiva. D'altro canto, le regole vengono applicate rigidamente, fino al punto di fornire file di intestazione "wrapper" con protezioni include e include raggruppate se qualche odioso sviluppatore di terze parti non si abbona alla mia visione :-)
Per aggiungere il mio mattone al muro.
Quindi di solito vado così:
// myproject/src/example.cpp
#include "myproject/example.h"
#include <algorithm>
#include <set>
#include <vector>
#include <3rdparty/foo.h>
#include <3rdparty/bar.h>
#include "myproject/another.h"
#include "myproject/specific/bla.h"
#include "detail/impl.h"
Ogni gruppo separato da una riga vuota da quella successiva:
Inoltre, a parte le intestazioni di sistema, ogni file si trova in una cartella con il nome del suo spazio dei nomi, solo perché è più facile rintracciarli in questo modo.
#define
quello che incasina altro codice) e per prevenire dipendenze implicite. Ad esempio, se il nostro file di intestazione della base di codice foo.h
dipende davvero, <map>
ma dovunque fosse usato nei .cc
file, <map>
è già stato incluso, probabilmente non lo noteremmo. Fino a quando qualcuno ha cercato di includere foo.h
senza prima includere <map>
. E poi sarebbero infastiditi.
.h
ha almeno uno .cpp
che lo include per primo (in effetti, nel mio codice personale il test unit associato lo include per primo, e il codice sorgente lo include nel suo legittimo gruppo ). Per quanto riguarda il fatto di non essere influenzato, se una qualsiasi delle intestazioni include, <map>
allora tutte le intestazioni incluse in seguito sono influenzate comunque, quindi mi sembra una battaglia persa.
Header corresponding to this cpp file first (sanity check)
. C'è qualcosa di particolare se #include "myproject/example.h"
viene spostato alla fine di tutte le inclusioni?
Io raccomando:
E, naturalmente, l'ordine alfabetico all'interno di ogni sezione, ove possibile.
Utilizzare sempre dichiarazioni anticipate per evitare messaggi non necessari #include
nei file di intestazione.
Sono abbastanza sicuro che questa non sia una pratica consigliata in qualsiasi parte del mondo sano, ma mi piace allineare il sistema in base alla lunghezza del nome del file, ordinato lessicamente nella stessa lunghezza. Così:
#include <set>
#include <vector>
#include <algorithm>
#include <functional>
Penso che sia una buona idea includere le proprie intestazioni prima di altre persone, per evitare la vergogna della dipendenza dall'ordine di inclusione.
windows.h
.
Questo non è soggettivo. Assicurati che le intestazioni non facciano affidamento sull'essere #include
in un ordine specifico. Puoi essere sicuro che non importa quale ordine includi le intestazioni STL o Boost.
In primo luogo includere l'intestazione corrispondente al .cpp ... in altre parole, source1.cpp
dovrebbe includere source1.h
prima di includere qualsiasi altra cosa. L'unica eccezione che mi viene in mente è quando si utilizza MSVC con intestazioni precompilate, nel qual caso, si è costretti a includere stdafx.h
prima di ogni altra cosa.
Ragionamento: l' inclusione del source1.h
precedente prima di qualsiasi altro file garantisce che possa essere autonomo senza dipendenze. Se source1.h
assume una dipendenza in una data successiva, il compilatore ti avviserà immediatamente di aggiungere le dichiarazioni forward richieste source1.h
. Questo a sua volta garantisce che le intestazioni possano essere incluse in qualsiasi ordine dai loro dipendenti.
Esempio:
source1.h
class Class1 {
Class2 c2; // a dependency which has not been forward declared
};
source1.cpp
#include "source1.h" // now compiler will alert you saying that Class2 is undefined
// so you can forward declare Class2 within source1.h
...
Utenti MSVC: consiglio vivamente di utilizzare intestazioni precompilate. Quindi, sposta tutte le #include
direttive per le intestazioni standard (e altre intestazioni che non cambieranno mai) in stdafx.h
.
Includi dal più specifico al meno specifico, iniziando con il corrispondente .hpp per il .cpp, se ne esiste uno. In questo modo, verranno rivelate eventuali dipendenze nascoste nei file di intestazione che non sono autosufficienti.
Ciò è complicato dall'uso di intestazioni precompilate. Un modo per aggirare questo è, senza rendere specifico il compilatore del progetto, è utilizzare una delle intestazioni del progetto poiché l'intestazione precompilata include il file.
È una domanda difficile nel mondo C / C ++, con così tanti elementi oltre lo standard.
Penso che l'ordine dei file di intestazione non sia un problema serio finché si compila, come ha detto squelart.
Le mie idee sono: se non vi è alcun conflitto di simboli in tutte quelle intestazioni, qualsiasi ordine è OK e il problema di dipendenza dell'intestazione può essere risolto in seguito aggiungendo #include righe al .h difettoso.
La vera seccatura sorge quando alcune intestazioni cambiano la sua azione (controllando le condizioni #if) in base a quali intestazioni sono sopra.
Ad esempio, in stddef.h in VS2005, c'è:
#ifdef _WIN64
#define offsetof(s,m) (size_t)( (ptrdiff_t)&(((s *)0)->m) )
#else
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
Ora il problema: se ho un'intestazione personalizzata ("custom.h") che deve essere utilizzata con molti compilatori, inclusi alcuni più vecchi che non forniscono offsetof
nelle loro intestazioni di sistema, dovrei scrivere nella mia intestazione:
#ifndef offsetof
#define offsetof(s,m) (size_t)&(((s *)0)->m)
#endif
E assicurati di dire all'utente di #include "custom.h"
dopo tutte le intestazioni di sistema, altrimenti la riga offsetof
in in stddef.h affermerà un errore di ridefinizione delle macro.
Preghiamo di non incontrare più questi casi nella nostra carriera.