Rientro #define


99

So che le #defines, ecc. Normalmente non sono mai rientrate. Perché?

Al momento sto lavorando su un codice che ha un'orribile miscela di #defines, #ifdefs, #elses, #endifs, ecc. Tutti questi spesso sono mescolati con il normale codice C. La mancata rientranza della #defines li rende difficili da leggere. E la combinazione di codice rientrato con #defines non rientrate è un incubo.

Qual è il vantaggio di non indentare #defines? Mi rende una persona cattiva se li indegno? Non è molto più carino?

#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

Risposte:


103

Il preprocessore Pre-ANSI C non consentiva lo spazio tra l'inizio di una riga e il carattere "#"; il "#" iniziale doveva sempre essere posizionato nella prima colonna.

I compilatori C pre-ANSI non esistono oggigiorno. Usa lo stile che preferisci (spazio prima di "#" o spazio tra "#" e l'identificatore).

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


26

Come alcuni hanno già detto, alcuni compilatori pre-ANSI richiedevano che # fosse il primo carattere sulla riga ma non richiedevano che la direttiva del preprocessore fosse allegata ad esso, quindi l'indentazione è stata fatta in questo modo.

#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

Ho spesso visto questo stile nelle vecchie intestazioni Unix, ma lo odio perché la colorazione della sintassi spesso fallisce su tale codice. Uso un colore molto visibile per la direttiva del pre-processore in modo che risaltino (sono a un meta-livello, quindi non dovrebbero far parte del normale flusso di codice). Puoi anche vedere che SO non colora la sequenza in modo utile.


16

Per quanto riguarda l'analisi delle direttive del preprocessore, lo standard C99 (e lo standard C89 prima di esso) erano chiari sulla sequenza delle operazioni eseguite logicamente dal compilatore. In particolare, credo significhi che questo codice:

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

è equivalente a:

#include <stdio.h>

Nel bene e nel male, GCC 3.4.4 con '-std = c89 -pedantic' accetta la riga carica di commenti, in ogni caso. Non lo sto sostenendo come uno stile - non per un secondo (è orribile). Penso solo che sia possibile.

ISO / IEC 9899: 1999 sezione 5.1.1.2 Fasi di traduzione dice:

  1. [Mappatura dei caratteri, compresi i trigrafi]

  2. [Unione di righe - rimozione della nuova riga con barra rovesciata]

  3. Il file di origine viene scomposto in token di pre-elaborazione e sequenze di caratteri di spazio vuoto (inclusi i commenti). Un file sorgente non deve terminare in un token di pre-elaborazione parziale o in un commento parziale. Ogni commento è sostituito da uno spazio. I caratteri di nuova riga vengono mantenuti. L'implementazione definisce se ogni sequenza non vuota di caratteri di spazio vuoto diverso da new-line viene mantenuta o sostituita da un carattere di spazio.

  4. Le direttive di pre-elaborazione vengono eseguite, le chiamate di macro vengono espanse, [...]

La sezione 6.10 Direttive di pre-elaborazione dice:

Una direttiva di pre-elaborazione consiste in una sequenza di token di pre-elaborazione che inizia con un # token di pre-elaborazione che (all'inizio della fase di traduzione 4) è il primo carattere nel file sorgente (opzionalmente dopo uno spazio bianco che non contiene caratteri di nuova riga) o quello segue uno spazio bianco contenente almeno un carattere di nuova riga e termina con il successivo carattere di nuova riga.

L'unica controversia possibile è l'espressione tra parentesi '(all'inizio della fase di traduzione 4)', che potrebbe significare che i commenti prima dell'hash devono essere assenti poiché non sono altrimenti sostituiti da spazi fino alla fine della fase 4.

Come altri hanno notato, i preprocessori C pre-standard non si comportavano in modo uniforme in molti modi, e gli spazi prima e nelle direttive del preprocessore erano una delle aree in cui compilatori diversi facevano cose diverse, incluso il non riconoscere le direttive del preprocessore con spazi davanti a loro .

È interessante notare che la rimozione del backslash-newline avviene prima che i commenti vengano analizzati. Di conseguenza, non terminare i //commenti con una barra rovesciata.


7

Non so perché non sia più comune. Ci sono certamente momenti in cui mi piace indentare le direttive del preprocessore.

Una cosa che continua a intralciarmi (e talvolta mi convince a smettere di provare) è che molti o la maggior parte degli editor / IDE lanciano la direttiva nella colonna 1 alla minima provocazione. Che è fastidioso da morire.


5

In questi giorni credo che questa sia principalmente una scelta di stile. Penso che ad un certo punto nel lontano passato, non tutti i compilatori supportassero la nozione di indentazione definita dal preprocessore. Ho fatto alcune ricerche e non sono stato in grado di sostenere tale affermazione. Ma in ogni caso, sembra che tutti i compilatori moderni supportino l'idea di indentare la macro del pre-processore. Non ho una copia dello standard C o C ++, quindi non so se questo sia un comportamento standard o meno.

Quanto al fatto che sia o meno un buon stile. Personalmente mi piace l'idea di tenerli tutti a sinistra. Ti dà un posto coerente per cercarli. Sì, può diventare fastidioso quando ci sono macro molto annidate. Ma se li indenterai, alla fine ti ritroverai con un codice ancora più strano.

#if COND1
void foo() {
  #if COND2
  int i;
    #if COND3
  i = someFunction()
  cout << i << eol;
    #endif
  #endif
}
#endif

14
Il motivo per cui questo codice sembra strano è perché hai creato due "flussi" di rientro. Rientro la riga 4 di un altro livello e le righe 6 e 7 di altri due livelli.
Kevin Laity

3
Completamente d'accordo. A volte metto anche le parentesi graffe in modo che il # if assomigli all'if.
baash05

3
Provo molto difficile da organizzare il mio codice in modo che abbia alcun #ifdef linee nelle parti in cui ho codice vero e proprio. Invece, se ho bisogno di cose condizionali, le metto in funzioni fattorizzate o macro scomposte; è molto più chiaro in questo modo che trovo (beh, almeno lo è per me). Idealmente, tutte quelle parti scomposte saranno in altri file (intestazioni o file sorgente compilati in modo condizionale; la solita "condizione" è la piattaforma per cui il codice viene costruito).
Donal Fellows

2
Indicherei le righe 4 di un livello e le righe 6 e 7 di due livelli.
Rocketmagnet

3

Per l'esempio che hai fornito potrebbe essere appropriato usare l'indentazione per renderlo più chiaro, visto che hai una struttura così complessa di direttive annidate.

Personalmente penso che sia utile mantenerli non rientrati la maggior parte del tempo, perché queste direttive operano separatamente dal resto del codice. Direttive come #ifdef sono gestite dal pre-processore, prima che il compilatore possa vedere il tuo codice, quindi un blocco di codice dopo una direttiva #ifdef potrebbe non essere nemmeno compilato .

Mantenere le direttive visivamente separate dal resto del codice è più importante quando sono intervallate da codice (piuttosto che da un blocco di direttive dedicato, come nell'esempio fornito).


3
Dal punto di vista dell'IP, qual è la differenza tra qualcosa che non è compilato e qualcosa che non viene raggiunto a causa di un jmp.
baash05

2

Al momento sto lavorando a un codice che contiene un'orribile miscela di #defines, #ifdefs, #elses, #endifs, #etc. Tutti questi spesso si mescolano con il normale codice C. Il mancato rientro delle #define le rende difficili da leggere. E la combinazione di codice indentato con #defines non rientrati è un incubo.

Una soluzione comune è commentare le direttive, in modo da sapere facilmente a cosa si riferiscono:

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

#ifndef FOO
/* a lot of code */
#endif /* not FOO */

6
Ho visto quello stile, il mio capo lo usa. E, come il resto del suo codice, fa solo un pasticcio. Immagina di rimuovere tutto il rientro dalle tue normali istruzioni if ​​() e di utilizzare invece quei commenti. Ti lamenterai del fatto che non puoi vedere facilmente a cosa si riferiscono.
Rocketmagnet

2

In quasi tutti i compilatori C / CPP attualmente disponibili non è limitato. Spetta all'utente decidere come allineare il codice. Così felice codifica.


1
Risposta decente. Potresti migliorarlo aggiungendo qualche riferimento specifico alla guida di stile?
EtherDragon
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.