2013-03-07 13 views
14

Inizializzazione un array (in C++, ma qualsiasi soluzione che funziona per C sarà probabilmente funzionerà anche qui) con meno inizializzatori di quanto non sia elementi è perfettamente legale:Avere compilatore controllare il numero di inizializzatori di array

int array[10] = { 1, 2, 3 }; 

Tuttavia, questa può essere una fonte di bug oscuri. C'è un modo per fare in modo che il compilatore (gcc) controlli il numero di inizializzatori per uno specifico array ed emetta un avviso o anche un errore se la dimensione dichiarata e effettiva non corrisponde?

So che è possibile utilizzare int array[] = { 1, 2, 3 }; e quindi utilizzare asserzioni statiche che includono sizeof(array) per verificare le mie aspettative in quel punto. Ma sto usando array in altre unità di traduzione, quindi devo dichiararlo con una dimensione esplicita. Quindi questo trucco non funzionerà per me.

+4

Non sono sicuro di un avviso, ma qualsiasi compilatore che ha fornito un errore per questo sarebbe un compilatore non conforme. Non riesco a immaginare un fornitore di compilatori che aggiunge una tale opzione al loro prodotto, ecco a cosa servono gli strumenti di analisi statica. – Jon

+2

GCC dispone di '-Inseritori campo-di-creazione ', ma funziona solo per altri aggregati, non per gli array, probabilmente perché la maggior parte delle persone non vuole che avvisi gli array. Non è possibile utilizzare i test unitari per garantire che l'array contenga i valori corretti e che gli elementi finali non siano stati inizializzati a zero? –

+0

@JonathanWakely A sua volta, 'std :: array' è un aggregato! (A me non piace quell'avvertimento in tandem con esso in effetti.) –

risposta

4

(promossa da un commento come richiesto)

Se i valori nella matrice sono importanti per il corretto funzionamento del sistema, e avendo a zero-inizializzati i valori alla fine provoca insetti, quindi vorrei solo aggiungere una unit test per verificare che la matrice contenga i dati corretti, invece di provare a imporla nel codice.

2

Ho un'idea.

#define C_ASSERT(expr) extern char CAssertExtern[(expr)?1:-1] 

#define NUM_ARGS__(X, \ 
         N64,N63,N62,N61,N60, \ 
    N59,N58,N57,N56,N55,N54,N53,N52,N51,N50, \ 
    N49,N48,N47,N46,N45,N44,N43,N42,N41,N40, \ 
    N39,N38,N37,N36,N35,N34,N33,N32,N31,N30, \ 
    N29,N28,N27,N26,N25,N24,N23,N22,N21,N20, \ 
    N19,N18,N17,N16,N15,N14,N13,N12,N11,N10, \ 
    N09,N08,N07,N06,N05,N04,N03,N02,N01, N, ...) N 

#define NUM_ARGS(...) \ 
    NUM_ARGS__(0, __VA_ARGS__, \ 
       64,63,62,61,60, \ 
    59,58,57,56,55,54,53,52,51,50, \ 
    49,48,47,46,45,44,43,42,41,40, \ 
    39,38,37,36,35,34,33,32,31,30, \ 
    29,28,27,26,25,24,23,22,21,20, \ 
    19,18,17,16,15,14,13,12,11,10, \ 
    9, 8, 7, 6, 5, 4, 3, 2, 1, 0) 

#define DECL_INIT_ARRAYN(TYPE, NAME, COUNT, N, ...) \ 
    C_ASSERT(COUNT == N); \ 
    TYPE NAME[COUNT] = { __VA_ARGS__ } 

#define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...) \ 
    DECL_INIT_ARRAYN(TYPE, NAME, COUNT, NUM_ARGS(__VA_ARGS__), __VA_ARGS__) 

DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3); 

int main(void) 
{ 
    DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4); 
    DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6); 
    return 0; 
} 

uscita (ideone):

prog.c: In function ‘main’: 
prog.c:33:3: error: size of array ‘CAssertExtern’ is negative 
prog.c:34:3: error: size of array ‘CAssertExtern’ is negative 
prog.c:34:3: error: excess elements in array initializer [-Werror] 
prog.c:34:3: error: (near initialization for ‘array5_6’) [-Werror] 
prog.c:34:3: error: unused variable ‘array5_6’ [-Werror=unused-variable] 
prog.c:33:3: error: unused variable ‘array5_4’ [-Werror=unused-variable] 
prog.c:34:3: error: unused variable ‘CAssertExtern’ [-Werror=unused-variable] 
cc1: all warnings being treated as errors 

UPD: Il PO ha trovato una soluzione più breve C++ 11, sulla base della stessa idea di utilizzare __VA_ARGS__ e statico/asserzione in fase di compilazione:

#include <tuple> 

#define DECL_INIT_ARRAY(TYPE, NAME, COUNT, ...)       \ 
    static_assert(COUNT ==            \ 
    std::tuple_size<decltype(std::make_tuple(__VA_ARGS__))>::value,  \ 
    "Array " #NAME " should have exactly " #COUNT " initializers");  \ 
    TYPE NAME[COUNT] = { __VA_ARGS__ } 

DECL_INIT_ARRAY(const int, array3_3, 3, 1, 2, 3); 

int main(void) 
{ 
    DECL_INIT_ARRAY(const int, array5_4, 5, 1, 2, 3, 4); 
    DECL_INIT_ARRAY(const int, array5_6, 5, 1, 2, 3, 4, 5, 6); 
    return 0; 
} 

Uscita (ideone):

prog.cpp: In function ‘int main()’: 
prog.cpp:13:3: error: static assertion failed: Array array5_4 should have exactly 5 initializers 
prog.cpp:14:3: error: static assertion failed: Array array5_6 should have exactly 5 initializers 
prog.cpp:14:3: error: too many initializers for ‘const int [5]’ 
prog.cpp:13:3: warning: unused variable ‘array5_4’ [-Wunused-variable] 
prog.cpp:14:3: warning: unused variable ‘array5_6’ [-Wunused-variable] 
+2

+1 per usare un bazooka per uccidere una mosca :) –

+0

@DavidRF LOL! :) –

+0

Nel mio caso, 'COUNT' è diverse migliaia, quindi i macro di conteggio per quello sarebbero piuttosto lunghi. Ma forse c'è un modo C++ 11 per farlo funzionare? Qualcosa sulla falsariga di 'std :: tuple_size :: valore' o simile. – MvG

3

Dal momento che si sta utilizzando array in altre unità di traduzione, è a quanto pare collegamento esterno. In questo caso, sei autorizzato a dichiararlo due volte, a condizione che le dichiarazioni lo diano dello stesso tipo. Quindi, basta dichiararlo due volte, una volta permettendo al compilatore di contare gli inizializzatori e una volta specificando la dimensione. Mettete questa riga in un file sorgente, prima di ogni intestazione che dichiara array:

int array[] = { 1, 2, 3 }; 

Più tardi nello stesso file, mettere una linea #include che dichiara array, con una linea come ad esempio:

extern int array[10]; 

Se le dimensioni dell'array differiscono nelle due dichiarazioni, il compilatore segnalerà un errore. Se sono uguali, il compilatore li accetterà.

+0

Una di quelle funzioni spesso trascurate. :) È lo stesso in C e C++, btw? –

+0

@AlexeyFrunze: Non proprio uguale; C++ considera entrambe le righe come definizioni, ma l'inserimento di 'extern' all'inizio della seconda riga lo rende una dichiarazione che non è una definizione. –

+0

Bello, ho provato questo, ma ho messo le due dichiarazioni al contrario, che _non forniscono una diagnostica (per ovvi motivi ora ci penso) –

Problemi correlati