2013-03-07 9 views
14

Inicjowanie tablicy (w C++, ale każde rozwiązanie, które działa na C będzie prawdopodobnie działa tutaj również) z mniej inicjalizatorów niż to ma elementy doskonale prawna:Czy kompilator sprawdzić liczbę inicjalizatorów tablicy

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

Jednak może to być źródłem niejasnych błędów. Czy istnieje sposób, aby kompilator (gcc) sprawdził liczbę inicjalizatorów dla jednej konkretnej tablicy i wygenerował ostrzeżenie, a nawet błąd, jeśli deklarowany i rzeczywisty rozmiar nie są zgodne?

Wiem, że mogę użyć int array[] = { 1, 2, 3 }; i mogę wtedy użyć statycznych twierdzeń dotyczących sizeof(array), aby zweryfikować moje oczekiwania. Ale używam array w innych jednostkach tłumaczeniowych, więc muszę zadeklarować je z wyraźnym rozmiarem. Ta sztuczka nie zadziała dla mnie.

+4

Nie jestem pewien ostrzeżenia, ale każdy kompilator, który podałby błąd, byłby niezgodnym kompilatorem. Nie mogę sobie wyobrazić dostawcy kompilatora, który dodałby taką opcję do swojego produktu, do tego służą narzędzia do analizy statycznej. – Jon

+2

GCC ma "-Wissing-field-initializers", ale działa tylko dla innych agregatów, a nie tablic, prawdopodobnie dlatego, że większość ludzi nie chce, aby ostrzegał o tablicach. Nie możesz użyć testów jednostkowych, aby upewnić się, że tablica zawiera prawidłowe wartości, a elementy końcowe nie zostały zainicjalizowane zerową wartością? –

+0

@ JonathanWakely Z kolei 'std :: array' jest agregatem! (Nie podoba mi się to ostrzeżenie w tandemie z nim.) –

Odpowiedz

4

(awans z komentarzem jako wymagane)

Jeśli wartości w tablicy są ważne dla poprawnego działania systemu, a mającym zerowe przygotowanej wartości na koniec powoduje błędy, to chciałbym tylko dodać test jednostki, aby sprawdzić, czy tablica zawiera właściwe dane, zamiast próbować wymusić je w kodzie.

2

Mam pomysł.

#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; 
} 

wyjściowa (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: PO znalazła krótszy C++ 11 rozwiązania, opierając się na tej samej idei wykorzystania __VA_ARGS__ i statyczne/Twierdzenie o kompilacji:

#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; 
} 

Dane wyjściowe (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 za użycie bazooki do zabicia muchy :) –

+0

@DavidRF LOL! :) –

+0

W moim przypadku 'COUNT' ma kilka tysięcy, więc makra liczące to będą dość długie. Ale może istnieje sposób C++ 11, aby to zadziałało? Coś podobnego do 'std :: tuple_size :: value' lub podobne. – MvG

3

Ponieważ używasz array w innych jednostek tłumaczeniowych, to widocznie ma zewnętrzne powiązania. W takim przypadku możesz zadeklarować go dwukrotnie, o ile deklaracje nadają mu ten sam typ. Po prostu zadeklaruj go dwukrotnie, raz pozwalając kompilatorowi policzyć inicjalizatory i po określeniu rozmiaru. Umieścić tę linię w jednym pliku źródłowym, przed każdym nagłówku, który deklaruje array:

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

Później w tym samym pliku, położyć #include linię, która deklaruje array z linii takich jak:

extern int array[10]; 

Jeśli rozmiary tablic różnią się w dwóch deklaracjach, kompilator zgłosi błąd. Jeśli są takie same, kompilator je zaakceptuje.

+0

Jedna z tych często zaniedbanych funkcji. :) Czy to samo w C i C++, btw? –

+0

@AlexeyFrunze: Nie całkiem taki sam; C++ traktuje obie linie jako definicje, ale wstawienie "extern" na początku drugiego wiersza powoduje, że deklaracja ta nie jest definicją. –

+0

Dobrze, próbowałem tego, ale umieściłem dwie deklaracje w odwrotnym kierunku, które nie dają diagnozy (z oczywistego powodu, teraz o tym myślę) –

Powiązane problemy