Domanda

Ho una base di codice molto grande (leggi: migliaia di moduli) con codice condiviso tra numerosi progetti che funzionano tutti su sistemi operativi diversi con compilatori C ++ diversi. Inutile dirlo, mantenere il processo di compilazione può essere piuttosto un lavoro ingrato.

Esistono diversi punti nella base di codice in cui si potrebbe ripulire sostanzialmente il codice se solo ci fosse un modo per fare in modo che il pre-processore ignori alcuni #includes se il file non esistesse nel cartella corrente. Qualcuno conosce un modo per raggiungere questo obiettivo?

Attualmente, usiamo un #ifdef attorno al #include nel file condiviso, con un secondo file specifico del progetto che #definisce se #include esiste nel progetto. Funziona, ma è brutto. Le persone spesso dimenticano di aggiornare correttamente le definizioni quando aggiungono o rimuovono file dal progetto. Ho pensato di scrivere uno strumento pre-build per mantenere aggiornato questo file, ma se esiste un modo indipendente dalla piattaforma per farlo con il preprocessore, preferirei piuttosto farlo in questo modo. Qualche idea?

È stato utile?

Soluzione

Generalmente questo viene fatto usando uno script che tenta di eseguire il preprocessore nel tentativo di includere il file. A seconda che il preprocessore restituisca un errore, lo script aggiorna un file .h generato con un #define (o #undef) appropriato. In bash, la sceneggiatura potrebbe apparire vagamente così:

cat > .test.h <<'EOM'
#include <asdf.h>
EOM
if gcc -E .test.h
 then
  echo '#define HAVE_ASDF_H 1' >> config.h
 else 
  echo '#ifdef HAVE_ASDF_H' >> config.h
  echo '# undef HAVE_ASDF_H' >> config.h
  echo '#endif' >> config.h
 fi

Un framework abbastanza completo per lavorare in modo portabile con controlli di portabilità come questo (e migliaia di altri) è autoconf .

Altri suggerimenti

Piccolo aggiornamento

Alcuni compilatori potrebbero supportare __has_include (header-name) .

L'estensione è stata aggiunta allo C ++ 17 standard ( P0061R1 ).

Supporto compilatore

  • Clang
  • GCC da 5.X
  • Visual Studio da VS2015 Update 2 (?)

Esempio (dal sito web di clang):

// Note the two possible file name string formats.
#if __has_include("myinclude.h") && __has_include(<stdint.h>)
# include "myinclude.h"
#endif

Fonti

Crea una cartella speciale per le intestazioni mancanti e crea l'ultima cartella da cercare
(che è specifico del compilatore - l'ultimo elemento nella variabile d'ambiente " INCLUDE " qualcosa del genere)

Quindi, se manca un header1.h, crea in quella cartella uno stub

header1.h:

#define header1_is_missing

Ora puoi sempre scrivere

#include <header1.h>
#ifdef header1_is_missing

   // there is no header1.h 

#endif

Il preprocessore stesso non è in grado di identificare l'esistenza di file, ma è certamente possibile utilizzare l'ambiente di compilazione per farlo. Ho principalmente familiarità con make, che ti permetterebbe di fare qualcosa del genere nel tuo makefile:

ifdef $(test -f filename && echo "present")
  DEFINE=-DFILENAME_PRESENT
endif

Naturalmente, dovresti trovare un analogo a questo in altri ambienti di build come VisualStudio, ma sono sicuro che esistono.

È possibile eseguire una fase di pre-build che genera un file include che contiene un elenco di #define che rappresentano i nomi dei file esistenti nella directory corrente:

#define EXISTS_FILE1_C
#define EXISTS_FILE1_H
#define EXISTS_FILE2_C

Quindi, includi quel file dal tuo codice sorgente, e quindi il tuo sorgente può testare il EXISTS_ * per vedere se esiste un file o meno.

Per quanto ne so cpp non ha una direttiva sull'esistenza di un file.

Potresti riuscire a farlo con un po 'di aiuto dal Makefile, se stai usando la stessa marca su più piattaforme. Puoi rilevare la presenza di un file nel Makefile:

foo.o: foo.c
    if [ -f header1.h ]; then CFLAGS+=-DHEADER1_INC

Come menziona @Greg Hewgill, puoi quindi rendere condizionati i tuoi #includi:

#ifdef HEADER1_INC
#include <header1.h>
#endif

Un'altra possibilità: popolare una directory da qualche parte con versioni di lunghezza zero di tutte le intestazioni che si desidera includere facoltativamente. Passa un argomento -I a questa directory come ultima tale opzione.

GCC cpp cerca le sue directory include in ordine, se trova un file header in una directory precedente lo utilizzerà. Altrimenti, alla fine troverà il file di lunghezza zero e sarà felice.

Presumo che anche altre implementazioni cpp cerchino le loro directory include nell'ordine specificato.

Ho dovuto fare qualcosa di simile per il sistema operativo Symbian. Ecco come l'ho fatto: supponiamo che tu voglia verificare se il file " file_strange.h " esiste e si desidera includere alcune intestazioni o collegamenti ad alcune librerie a seconda dell'esistenza di quel file.

prima crea un piccolo file batch per verificare l'esistenza di quel file.

autoconf è buono, ma è un'uccisione eccessiva per molti piccoli progetti.

---------- check.bat

@echo off

IF EXIST [\epoc32\include\domain\middleware\file_strange] GOTO NEW_API
GOTO OLD_API
GOTO :EOF

:NEW_API
echo.#define NEW_API_SUPPORTED>../inc/file_strange_supported.h
GOTO :EOF

:OLD_API
echo.#define OLD_API_SUPPORTED>../inc/file_strange_supported.h
GOTO :EOF

---------- check.bat termina

quindi ho creato un file gnumake

---------- checkmedialist.mk

do_nothing :
    @rem do_nothing

MAKMAKE : 
        check.bat

BLD : do_nothing

CLEAN : do_nothing

LIB : do_nothing

CLEANLIB : do_nothing

RESOURCE : do_nothing

FREEZE : do_nothing

SAVESPACE : do_nothing

RELEASABLES : do_nothing

FINAL : do_nothing

----------check.mk termina

includi il file check.mk nel tuo file bld.inf, DEVE trovarsi prima dei tuoi file MMP

PRJ_MMPFILES
gnumakefile checkmedialist.mk

ora in fase di compilazione il file file_strange_supported.h avrà un flag appropriato impostato. puoi usare questo flag nei tuoi file cpp o anche nel file mmp ad esempio in mmp

#include "../inc/file_strange_supported.h"
#ifdef NEW_API_SUPPORTED
LIBRARY newapi.lib
#else
LIBRARY oldapi.lib
#endif

e in .cpp

#include "../inc/file_strange_supported.h"
#ifdef NEW_API_SUPPORTED
CStrangeApi* api = Api::NewLC();
#else
// ..
#endif

Contrariamente ad alcune affermazioni qui e su Internet, Visual Studio 2015 NON supporta la funzione __has_include - almeno secondo la mia esperienza. Testato con l'aggiornamento 3.

Le voci potrebbero essere derivate dal fatto che VS 2017 è anche indicato come "Versione 15"; VS 2015 è invece indicato come "Versione 14". Il supporto per la funzionalità sembra essere stato ufficialmente introdotto con " Visual Studio 2017 versione 15.3 " ;.

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