2015-06-18 16 views
7

Aggiungo controlli in fase di compilazione ai progetti C++ della mia azienda per assicurarmi che le librerie di terze parti su tutte le macchine di sviluppo e sui server di compilazione siano aggiornate. La maggior parte delle librerie definisce qualcosa come il seguente per es. Versione 3.1.4:Come verificare un valore come "#define VERSION 3.1.4" al momento della compilazione?

#define VERSION_MAJOR 3 
#define VERSION_MINOR 1 
#define VERSION_BUILD 4 

Questo è bello e facile da controllare usando static_assert o preprocessore direttive.

ora sto guardando una libreria di terze parti che definisce un unico macro invece:

#define VERSION 3.1.4 

Come posso verificare il valore di una macro al momento della compilazione?


Con C++ 11, ho potuto utilizzare una funzione di confronto constexpr stringa e stringa i la macro di check it:

constexpr bool static_equal(const char * a, const char * b) 
{ 
    return (*a == *b) && (*a == '\0' || static_equal(a + 1, b + 1)); 
} 

// stringification functions 
#define str(x) #x 
#define xstr(x) str(x) 

static_assert(static_equal(xstr(VERSION), "3.1.4"), "incorrect version of libwhatever"); 

Ma stiamo utilizzando Visual Studio 2013 sulle macchine Windows, quindi posso usare solo il sottoinsieme di C++ 11 che supporta. Sfortunatamente constexpr non è supportato.

+0

Potrebbe essere più semplice conservare un altro file in cui la versione è suddivisa come preferisci. –

+0

Nel preprocessore non puoi aver paura. I confronti funzionano solo sugli interi e non c'è modo di dividere il token 3.1.4. Dovrà essere un passaggio di elaborazione del testo esterno di qualche tipo, o controllare in fase di esecuzione. VS2015 rc è disponibile e potrebbe supportare constexpr. –

+0

@RSahu: questa è una libreria di terze parti. Se volessi aggiungervi dei file, dovrei creare pacchetti personalizzati e costringere gli sviluppatori a usarli. Questo è molto più noioso del download dalle fonti originali, oppure, per Debian e derivati, 'apt-get libwhatever-dev'. – gnome

risposta

2

Qui è quello che sto facendo ora:

#define str(x) #x 
#define xstr(x) str(x) 

#include xstr(libwhatever.version.is.VERSION.should.be.3.1.4) 

Insieme a questo, aggiungo un file vuoto chiamato libwhatever.version.is.3.1.4.should.be.3.1.4 al progetto. Quindi, se la versione è corretta, il preprocessore includerà correttamente questo file. Altrimenti, fallirà con "Impossibile aprire" libwhatever.version.is.2.7.2.should.be.3.1.4 ', nessun file o directory ". E fallire la costruzione con un messaggio un po 'significativo è ciò che conta alla fine.

Naturalmente questo approccio non è molto flessibile; ad esempio non riesco a verificare una versione minima o una gamma di versioni. Ma per me è sufficiente essere in grado di verificare il valore esatto.

Questo sembra funzionare con Visual C++ e con g ++. Non sono sicuro che il comportamento sia del tutto ben definito secondo lo standard, comunque.

1

Non è possibile nel preprocessore, ma è possibile tratti di tipo di abuso!

VS 2013 sembra supportare i modelli variadic. Provare a utilizzare la macro CSTRING a https://stackoverflow.com/a/15912824/2097780 (si dovrebbe essere in grado di sostituire constexpr con const e hanno il codice continuerà a funzionare) e fare qualcosa di simile:

#define STRT(x) decltype(CSTRING(x)) 
static_assert(std::is_same<STRT(VERSION), STRT("3.1.4")>::value, "incorrect version of libwhatever"); 

EDIT: che non funziona. Tuttavia, se il compilatore compila questo senza errori:

extern const char data[] = "abc"; 
template <char C> struct x { 
    static const char c = C; 
}; 
char buf[(int)x<"ABC123"[0]>::c]; 
int main() { return (int)buf; } 

allora si può provare questo:

#include <type_traits> 
#define VERSION 1.2.3 
#define STR2(x) #x 
#define STR(x) STR2(x) 

template <char...> struct ststring; 

// https://stackoverflow.com/a/15860416/2097780 

#define MACRO_GET_1(str, i) \ 
    (sizeof(str) > (i) ? str[(i)] : 0) 

#define MACRO_GET_4(str, i) \ 
    MACRO_GET_1(str, i+0), \ 
    MACRO_GET_1(str, i+1), \ 
    MACRO_GET_1(str, i+2), \ 
    MACRO_GET_1(str, i+3) 

#define MACRO_GET_16(str, i) \ 
    MACRO_GET_4(str, i+0), \ 
    MACRO_GET_4(str, i+4), \ 
    MACRO_GET_4(str, i+8), \ 
    MACRO_GET_4(str, i+12) 

#define MACRO_GET_64(str, i) \ 
    MACRO_GET_16(str, i+0), \ 
    MACRO_GET_16(str, i+16), \ 
    MACRO_GET_16(str, i+32), \ 
    MACRO_GET_16(str, i+48) 

#define MACRO_GET_STR(str) MACRO_GET_64(str, 0), 0 

static_assert(std::is_same<ststring<MACRO_GET_STR(STR(VERSION))>, 
          ststring<MACRO_GET_STR("1.2.3")>>::value, 
       "invalid library version"); 
+0

'decltype' controllerà solo la lunghezza della stringa letterale, non il contenuto del carattere –

+0

@BenVoigt No non lo farà. Ho usato questo nel mio codice prima. 'decltype' restituirà' string <'3', '.', '1', '.', '4'> 'o simile. – refi64

+0

Oh, la macro CSTRING è altamente magica. No, non funzionerà senza constexpr. –

1

Se si fa clic destro sul vostro Progetto-> Properties-> Crea Events-> Pre Build Event Vedrai un'opzione che dice "Command Line". Puoi mettere una chiamata a un altro programma qui.

È possibile scrivere un altro programma in C++ o qualsiasi lingua si preferisca che controlli il file (o qualsiasi numero di file che si desidera) per "#define VERSION 3.1.4". Puoi interrompere la tua compilazione e inserire gli avvisi che desideri in quel programma.

ecco un tutorial: https://dillieodigital.wordpress.com/2012/11/27/quick-tip-aborting-builds-in-visual-studio-based-on-file-contents/

lettura correlati: https://msdn.microsoft.com/en-us/library/e85wte0k.aspx

Ho provato a fare scherzi con i comandi del preprocessore per lungo tempo, e non riuscivo a trovare un modo per farlo utilizzando solo comandi del preprocessore.

Problemi correlati