Wcięcie #defines

99

Wiem, że #defines itp. Zwykle nie są wcięte. Czemu?

W tej chwili pracuję nad jakimś kodem, który ma okropną mieszankę #defines, #ifdefs, #elses, #endifs itd. Wszystko to często jest pomieszane z normalnym kodem C. Brak wcięć w literach #definesprawia, że ​​są one trudne do odczytania. A mieszanka kodu z wcięciem #definei bez wcięć to koszmar.

Jaka jest korzyść z braku wcięć #define? Czy to czyni mnie złą osobą, gdy ich wciskam? Czy to nie jest dużo przyjemniejsze?

#ifdef SDCC
    #if DEBUGGING == 1
        #if defined (pic18f2480)
            #define FLASH_MEMORY_END 0x3DC0
        #elif defined (pic18f2580)
            #define FLASH_MEMORY_END 0x7DC0
        #else
            #error "Can't set  up flash memory end!"
        #endif
    #else
        #if defined (pic18f2480)
            #define FLASH_MEMORY_END 0x4000
        #elif defined (pic18f2580)
            #define FLASH_MEMORY_END 0x8000
        #else
            #error "Can't set  up flash memory end!"
        #endif
    #endif
#else
    #if DEBUGGING == 1
        #define FLASH_MEMORY_END 0x7DC0
    #else
        #define FLASH_MEMORY_END 0x8000
    #endif
#endif
Magnes rakietowy
źródło

Odpowiedzi:

103

Preprocesor Pre-ANSI C nie pozwalał na odstęp między początkiem wiersza a znakiem „#”; wiodący „#” musiał zawsze znajdować się w pierwszej kolumnie.

W dzisiejszych czasach kompilatory przed ANSI C nie istnieją. Użyj dowolnego preferowanego stylu (spacja przed „#” lub spacja między „#” a identyfikatorem).

http://www.delorie.com/gnu/docs/gcc/cpp_48.html


źródło
26

Jak niektórzy już powiedzieli, niektóre kompilatory Pre-ANSI wymagały, aby # był pierwszym znakiem w linii, ale nie wymagały dołączenia do niego dyrektywy de preprocessor, więc wcięcie zostało wykonane w ten sposób.

#ifdef SDCC
#  if DEBUGGING == 1
#    if defined (pic18f2480)
#      define FLASH_MEMORY_END 0x3DC0
#    elif defined (pic18f2580)
#      define FLASH_MEMORY_END 0x7DC0
#    else
#      error "Can't set  up flash memory end!"
#    endif
#  else
#    if defined (pic18f2480)
#      define FLASH_MEMORY_END 0x4000
#    elif defined (pic18f2580)
#      define FLASH_MEMORY_END 0x8000
#    else
#      error "Can't set  up flash memory end!"
#    endif
#  endif
#else
#  if DEBUGGING == 1
#    define FLASH_MEMORY_END 0x7DC0
#  else
#    define FLASH_MEMORY_END 0x8000
#  endif
#endif

Często widziałem ten styl w starych nagłówkach Uniksa, ale nienawidzę go, ponieważ kolorowanie składni często zawodzi w takim kodzie. Używam bardzo widocznego koloru dla dyrektywy preprocesora, aby się wyróżniały (są na metapoziomie, więc nie powinny być częścią normalnego przepływu kodu). Możesz nawet zobaczyć, że SO nie koloruje sekwencji w użyteczny sposób.

Patrick Schlüter
źródło
16

Jeśli chodzi o analizę dyrektyw preprocesora, standard C99 (i poprzedni standard C89) jasno określał sekwencję operacji wykonywanych logicznie przez kompilator. W szczególności uważam, że oznacza to, że ten kod:

/* */ # /* */ include /* */ <stdio.h> /* */

jest równa:

#include <stdio.h>

Na dobre lub na złe, GCC 3.4.4 z „-std = c89 -pedantic” akceptuje przynajmniej wiersz z komentarzami. Nie zalecam tego jako stylu - ani przez sekundę (to jest okropne). Po prostu myślę, że jest to możliwe.

ISO / IEC 9899: 1999 sekcja 5.1.1.2 Fazy tłumaczenia mówi:

  1. [Mapowanie znaków, w tym trygrafy]

  2. [Łączenie wierszy - usuwanie odwrotnego ukośnika nowej linii]

  3. Plik źródłowy jest rozkładany na tokeny przetwarzania wstępnego i sekwencje białych znaków (w tym komentarze). Plik źródłowy nie może kończyć się częściowym znacznikiem wstępnego przetwarzania ani częściowym komentarzem. Każdy komentarz jest zastępowany jednym znakiem spacji. Znaki nowego wiersza są zachowywane. To, czy każda niepusta sekwencja znaków odstępu inna niż nowy wiersz jest zachowywana lub zastępowana przez jeden znak spacji, jest zdefiniowana w ramach implementacji.

  4. Dyrektywy przetwarzania wstępnego są wykonywane, wywołania makr są rozwijane, [...]

Sekcja 6.10 Dyrektywy dotyczące wstępnego przetwarzania mówi:

Dyrektywa preprocessing składa się z sekwencji tokenów przetwarzania wstępnego, która zaczyna się od tokenu # preprocessing, który (na początku fazy tłumaczenia 4) jest albo pierwszym znakiem w pliku źródłowym (opcjonalnie po spacji niezawierającej znaków nowego wiersza) lub następuje po białym znaku zawierającym co najmniej jeden znak nowego wiersza i kończy się następnym znakiem nowego wiersza.

Jedynym możliwym sporem jest sformułowanie w nawiasach „(na początku fazy tłumaczenia 4)”, co może oznaczać, że komentarze przed hashem muszą być nieobecne, ponieważ w przeciwnym razie nie są zastępowane spacjami do końca fazy 4.

Jak zauważyli inni, preprocesory C przed standardem nie zachowywały się jednakowo na wiele sposobów, a spacje przed i w dyrektywach preprocesora były jednym z obszarów, w których różne kompilatory robiły różne rzeczy, w tym nie rozpoznawały dyrektyw preprocesora ze spacjami przed nimi .

Warto zauważyć, że usuwanie odwrotnego ukośnika-nowej linii następuje przed analizą komentarzy. W związku z tym nie należy kończyć //komentarzy ukośnikiem odwrotnym.

Jonathan Leffler
źródło
7

Nie wiem, dlaczego nie jest to bardziej powszechne. Z pewnością są chwile, kiedy lubię wciskać dyrektywy preprocesora.

Jedyną rzeczą, która ciągle mi przeszkadza (i czasami przekonuje mnie, żebym przestał próbować) jest to, że wielu lub większość redaktorów / IDE wrzuci dyrektywę do kolumny 1 przy najmniejszej prowokacji. Co jest irytujące jak cholera.

Michael Burr
źródło
5

Obecnie uważam, że jest to głównie wybór stylu. Myślę, że w pewnym momencie w odległej przeszłości nie wszystkie kompilatory wspierały koncepcję wcięcia definicji preprocesora. Zrobiłem trochę badań i nie mogłem poprzeć tego twierdzenia. W każdym razie wydaje się, że wszystkie współczesne kompilatory wspierają ideę wcięcia makra preprocesora. Nie mam jednak kopii standardu C lub C ++, więc nie wiem, czy jest to standardowe zachowanie, czy nie.

Co do tego, czy to dobry styl. Osobiście podoba mi się pomysł trzymania ich wszystkich po lewej stronie. Daje ci spójne miejsce do ich poszukiwania. Tak, może to być denerwujące, gdy są bardzo zagnieżdżone makra. Ale jeśli je wcinasz, w końcu skończysz z jeszcze dziwniej wyglądającym kodem.

#if COND1
void foo() {
  #if COND2
  int i;
    #if COND3
  i = someFunction()
  cout << i << eol;
    #endif
  #endif
}
#endif
JaredPar
źródło
14
Ten kod wygląda dziwnie, ponieważ utworzyłeś dwa „strumienie” wcięć. Wcięłbym wiersz 4 o jeden poziom więcej, a wiersze 6 i 7 o dwa poziomy.
Kevin Laity
3
Kompletnie się zgadzam. Czasami nawet zakładam nawiasy klamrowe, aby # if wyglądało tak samo jak if.
baash05
3
Bardzo się staram ułożyć mój kod tak, aby nie #ifdef zawierał linii w częściach, w których mam rzeczywisty kod. Zamiast tego, jeśli potrzebuję elementów warunkowych, albo wyodrębniam to funkcje, albo makra; to jest o wiele jaśniejsze w ten sposób (cóż, przynajmniej dla mnie). Idealnie byłoby, gdyby wszystkie te wyodrębnione części znajdowały się w innych plikach (nagłówkach lub warunkowo skompilowanych plikach źródłowych; zwykle „warunkiem” jest platforma, dla której jest budowany kod).
Donal Fellows
2
Wcięłbym wiersze 4 na jeden poziom, a wiersze 6 i 7 na dwa poziomy.
Rocketmagnet,
3

Na przykład, który podałeś, może być właściwe użycie wcięć, aby było jaśniejsze, ponieważ masz tak złożoną strukturę zagnieżdżonych dyrektyw.

Osobiście uważam, że warto trzymać je bez wcięć przez większość czasu, ponieważ te dyrektywy działają niezależnie od reszty kodu. Dyrektywy, takie jak #ifdef, są obsługiwane przez preprocesor, zanim kompilator kiedykolwiek zobaczy Twój kod, więc blok kodu po dyrektywie #ifdef może nawet nie zostać skompilowany .

Utrzymywanie dyrektyw wizualnie oddzielonych od reszty kodu jest ważniejsze, gdy są one przeplatane kodem (zamiast dedykowanego bloku dyrektyw, jak w podanym przykładzie).

Daniel Fortunov
źródło
3
Z punktu widzenia adresu IP, jaka jest różnica między czymś, co nie jest skompilowane, a czymś, co nie zostało osiągnięte z powodu jmp.
baash05
2

W tej chwili pracuję nad kodem, który zawiera okropną mieszankę #defines, #ifdefs, #elses, #endifs, #etc. Wszystko to często miesza się z normalnym kodem C. Brak wcięć w #defines sprawia, że ​​są one trudne do odczytania. A mieszanka kodu z wcięciem i bez wcięć #defines to koszmar.

Typowym rozwiązaniem jest komentowanie dyrektyw, abyś mógł łatwo wiedzieć, do czego się odnoszą:

#ifdef FOO
/* a lot of code */
#endif /* FOO */

#ifndef FOO
/* a lot of code */
#endif /* not FOO */
Bastien Léonard
źródło
6
Widziałem ten styl, mój szef go używa. I podobnie jak reszta jego kodu po prostu robi bałagan. Wyobraź sobie usunięcie wszystkich wcięć z normalnych instrukcji if () i użycie tych komentarzy. Będziesz narzekać, że nie możesz łatwo zobaczyć, do czego się odnoszą.
Rocketmagnet,
2

W prawie wszystkich obecnie dostępnych kompilatorach C / CPP nie jest to ograniczone. To użytkownik decyduje, w jaki sposób chcesz wyrównać kod. Życzę miłego kodowania.

Hemanth
źródło
1
Przyzwoita odpowiedź. Czy mógłbyś to ulepszyć, dodając konkretne odniesienie do przewodnika po stylu?
EtherDragon