Perché un compilatore non può evitare di importare due volte un file header?


13

Nuovo in C ++! Quindi stavo leggendo questo: http://www.learncpp.com/cpp-tutorial/110-a-first-look-at-the-preprocessor/

Protezioni per la testata

Poiché i file di intestazione possono includere altri file di intestazione, è possibile finire nella situazione in cui un file di intestazione viene incluso più volte.

Quindi facciamo direttive per il preprocessore per evitarlo. Ma non sono sicuro - perché il compilatore non può ... non importare la stessa cosa due volte?

Dato che le protezioni per le intestazioni sono opzionali (ma apparentemente una buona pratica), mi viene quasi da pensare che ci siano scenari in cui si desidera importare qualcosa due volte. Anche se non riesco a pensare a nessuno di questi scenari. Qualche idea?


Sul compilatore MS c'è #pragma onceche dice al compilatore di includere quel file solo una volta.
CodesInChaos

Risposte:


27

Possono, come mostrato da nuove lingue che lo fanno.

Ma una decisione di progettazione è stata presa tanti anni fa (quando il compilatore C era costituito da più fasi indipendenti) e ora per mantenere la compatibilità il pre-processore deve agire in un certo modo per assicurarsi che le compilazioni del vecchio codice come previsto.

Poiché C ++ eredita il modo in cui elabora i file di intestazione da C, mantiene le stesse tecniche. Stiamo supportando una vecchia decisione di progettazione. Ma cambiare il modo in cui funziona è troppo rischioso che un sacco di codice potrebbe potenzialmente rompersi. Quindi ora dobbiamo insegnare ai nuovi utenti della lingua come usare le protezioni.

Ci sono un paio di trucchi con i file di intestazione dove li hai inclusi deliberatamente più volte (questo in realtà fornisce una funzione utile). Tuttavia, se riprogettassimo il paradigma da zero, potremmo renderlo un modo non predefinito di includere i file.


7

Altrimenti non sarebbe altrettanto espressivo, dato che hanno scelto di mantenere la compatibilità con C e quindi di continuare con un preprocessore piuttosto che un sistema di imballaggio tradizionale.

Una cosa che mi viene in mente è che avevo un progetto che era un'API. Ho avuto due file di intestazione x86lib.hex86lib_internal.h . Poiché internal era enorme, ho separato i bit "pubblici" in x86lib.h in modo che gli utenti non dovessero riservare tempo extra per la compilazione.

Ciò ha introdotto un problema divertente con le dipendenze, quindi alla fine ho avuto un flusso che è andato così in x86lib_internal

  1. Imposta la definizione del preprocessore INTERNO
  2. Includi x86lib.h (che era intelligente per agire in un certo modo quando è stato definito interno)
  3. Fai alcune cose e introduci alcune cose usate in x86lib.h
  4. Imposta DOPO la definizione del preprocessore
  5. Includi di nuovo x86lib.h (questa volta ignorerebbe tutto tranne una parte AFTER segregata che dipendeva da elementi di x86lib_internal

Non direi che era il modo migliore per farlo, ma ha raggiunto quello che volevo.


0

Una difficoltà con l'esclusione automatica delle intestazioni duplicate è che lo standard C è relativamente silenzioso in merito al significato di includere i nomi dei file. Ad esempio, supponiamo che il file principale in fase di compilazione contenga direttive #include "f1.h"e #include "f2.h"che entrambi i file trovati per tali direttive contengano #include "f3.h". Se f1.he si f2.htrovano in directory diverse, ma sono state trovate cercando i percorsi di inclusione, non sarebbe chiaro se le #includedirettive all'interno di quei file fossero destinate a caricare lo stesso f3.hfile o file diversi.

Le cose peggiorano ulteriormente se si aggiungono le possibilità di includere file, inclusi i percorsi relativi. In alcuni casi in cui i file di intestazione utilizzano percorsi relativi per le direttive di inclusione nidificate e quando si desidera evitare di apportare modifiche ai file di intestazione forniti, potrebbe essere necessario duplicare un file di intestazione in più punti all'interno della struttura di directory di un progetto. Anche se esistono più copie fisiche di quel file di intestazione, dovrebbero essere considerate semanticamente come se fossero un singolo file.

Se la #pragma oncedirettiva permettesse a un identificatore di seguire once, con la semantica che il compilatore dovrebbe saltare il file se l'identificatore corrisponde a uno di una #pragma oncedirettiva incontrata in precedenza , la semantica sarebbe inequivocabile; un compilatore che potrebbe dire che una #includedirettiva caricherà lo stesso #pragma oncefile con tag di uno precedente, potrebbe risparmiare un po 'di tempo saltando il file senza riaprirlo, ma tale rilevamento non sarebbe semanticamente importante poiché il file verrebbe ignorato se oppure no, il nome file è stato riconosciuto come una corrispondenza. Non sono a conoscenza di alcun compilatore che funzioni in questo modo, tuttavia. Avere un compilatore osservare se un file corrisponde allo schema #ifndef someIdentifier / #define someIdentifier / #endif [for that ifndef] / nothing followinge considerare una cosa equivalente a quanto sopra #pragma once someIdentifiersesomeIdentifier rimane definito, è essenzialmente altrettanto buono.

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.