Sangría #defines

99

Sé que las #defines, etc. normalmente nunca tienen sangría. ¿Por qué?

Estoy trabajando en un código en este momento que tiene una mezcla horrible de #defines, #ifdefs, #elses, #endifs, etc. Todo esto a menudo se mezcla con el código C normal. La falta de sangría de la #defines dificulta su lectura. Y la mezcla de código con sangría con #defines sin sangría es una pesadilla.

¿Cuál es el beneficio de no sangrar #defines? ¿Me convierte en una mala persona si los sangra? ¿No es esto mucho mejor?

#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
Cohetemagnet
fuente

Respuestas:

103

El preprocesador anterior a ANSI C no permitía espacios entre el comienzo de una línea y el carácter "#"; el "#" inicial siempre tenía que colocarse en la primera columna.

Los compiladores anteriores a ANSI C no existen en estos días. Utilice cualquier estilo (espacio antes de "#" o espacio entre "#" y el identificador) que prefiera.

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


fuente
26

Como algunos ya han dicho, algunos compiladores Pre-ANSI requerían que el # fuera el primer carácter de la línea, pero no requerían que se le adjuntara la directiva del preprocesador, por lo que la sangría se hizo de esta manera.

#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

A menudo he visto este estilo en los viejos encabezados de Unix, pero lo odio porque la coloración de la sintaxis a menudo falla en dicho código. Utilizo un color muy visible para la directiva de preprocesador para que se destaquen (están en un meta-nivel, por lo que no deberían ser parte del flujo normal de código). Incluso puede ver que SO no colorea la secuencia de una manera útil.

Patrick Schlüter
fuente
16

En cuanto al análisis sintáctico de las directivas del preprocesador, el estándar C99 (y el estándar C89 anterior) tenían claro la secuencia de operaciones que realizaba lógicamente el compilador. En particular, creo que significa que este código:

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

es equivalente a:

#include <stdio.h>

Para bien o para mal, GCC 3.4.4 con '-std = c89 -pedantic' acepta la línea cargada de comentarios, en cualquier caso. No estoy defendiendo eso como estilo, ni por un segundo (es espantoso). Simplemente creo que es posible.

ISO / IEC 9899: 1999 sección 5.1.1.2 Fases de traducción dice:

  1. [Mapeo de caracteres, incluidos trígrafos]

  2. [Empalme de línea: eliminación de la nueva línea de barra invertida]

  3. El archivo de origen se descompone en tokens de preprocesamiento y secuencias de caracteres de espacio en blanco (incluidos los comentarios). Un archivo fuente no debe terminar en un token de preprocesamiento parcial o en un comentario parcial. Cada comentario se reemplaza por un carácter de espacio. Se conservan los caracteres de nueva línea. Si cada secuencia no vacía de caracteres de espacio en blanco que no sea nueva línea se retiene o se reemplaza por un carácter de espacio, está definido por la implementación.

  4. Se ejecutan las directivas de preprocesamiento, se amplían las invocaciones de macros, [...]

La Sección 6.10 Directivas de preprocesamiento dice:

Una directiva de preprocesamiento consiste en una secuencia de tokens de preprocesamiento que comienza con un # token de preprocesamiento que (al comienzo de la fase de traducción 4) es el primer carácter en el archivo fuente (opcionalmente después del espacio en blanco que no contiene caracteres de nueva línea) o que sigue un espacio en blanco que contiene al menos un carácter de nueva línea y finaliza con el siguiente carácter de nueva línea.

La única disputa posible es la expresión entre paréntesis '(al inicio de la fase de traducción 4)', lo que podría significar que los comentarios antes del hash deben estar ausentes, ya que de lo contrario no se reemplazan por espacios hasta el final de la fase 4.

Como han señalado otros, los preprocesadores de C anteriores al estándar no se comportaban de manera uniforme de varias maneras, y los espacios antes y en las directivas del preprocesador eran una de las áreas en las que los diferentes compiladores hacían cosas diferentes, incluido no reconocer las directivas del preprocesador con espacios delante de ellas. .

Cabe destacar que la eliminación de la barra invertida y la nueva línea se produce antes de que se analicen los comentarios. En consecuencia, no debe terminar los //comentarios con una barra invertida.

Jonathan Leffler
fuente
7

No sé por qué no es más común. Ciertamente, hay ocasiones en las que me gusta aplicar sangría a las directivas del preprocesador.

Una cosa que se interpone en mi camino (y que a veces me convence de dejar de intentarlo) es que muchos o la mayoría de los editores / IDE arrojarán la directiva a la columna 1 a la menor provocación. Lo que es muy molesto.

Michael Burr
fuente
5

En estos días creo que esto es principalmente una elección de estilo. Creo que en un momento del pasado lejano, no todos los compiladores apoyaron la noción de indentar las definiciones del preprocesador. Investigué un poco y no pude respaldar esa afirmación. Pero en cualquier caso, parece que todos los compiladores modernos apoyan la idea de aplicar sangría a la macro de preprocesador. Sin embargo, no tengo una copia del estándar C o C ++, por lo que no sé si este es un comportamiento estándar o no.

En cuanto a si es de buen estilo o no. Personalmente, me gusta la idea de dejarlos todos a la izquierda. Le brinda un lugar consistente para buscarlos. Sí, puede resultar molesto cuando hay macros muy anidadas. Pero si los sangra, eventualmente terminará con un código de aspecto aún más extraño.

#if COND1
void foo() {
  #if COND2
  int i;
    #if COND3
  i = someFunction()
  cout << i << eol;
    #endif
  #endif
}
#endif
JaredPar
fuente
14
La razón por la que este código parece extraño es porque ha creado dos "secuencias" de sangría. Sangraría la línea 4 un nivel más, y sangraría las líneas 6 y 7 en dos niveles más.
Kevin Laity
3
Totalmente de acuerdo. A veces incluso pongo llaves para que los # if se vean como los if.
baash05
3
Trato muy duro para arreglar mi código para que tenga no hay #ifdef líneas en las partes en las que tengo código real. En cambio, si necesito cosas condicionales, las pongo en funciones factorizadas o macros factorizadas; es mucho más claro así lo encuentro (bueno, al menos lo es para mí). Idealmente, todas esas partes factorizadas estarán en otros archivos (encabezados o archivos fuente compilados condicionalmente; la "condición" habitual es para qué plataforma se está construyendo el código).
Donal Fellows
2
Sangraría las líneas 4 en un nivel y las líneas 6 y 7 en dos niveles.
Rocketmagnet
3

Para el ejemplo que ha dado, puede ser apropiado usar sangría para hacerlo más claro, ya que tiene una estructura tan compleja de directivas anidadas.

Personalmente, creo que es útil mantenerlos sin sangría la mayor parte del tiempo, porque estas directivas operan por separado del resto de su código. Las directivas como #ifdef son manejadas por el preprocesador, antes de que el compilador vea su código, por lo que un bloque de código después de una directiva #ifdef puede que ni siquiera se compile .

Mantener las directivas visualmente separadas del resto de su código es más importante cuando están intercaladas con código (en lugar de un bloque dedicado de directivas, como en el ejemplo que da).

Daniel Fortunov
fuente
3
Desde el punto de vista de la IP, ¿cuál es la diferencia entre algo que no está compilado y algo que no se alcanza debido a un jmp.
baash05
2

Estoy trabajando en un código en este momento que tiene una mezcla horrible de #defines, #ifdefs, #elses, #endifs, #etc. Todos estos a menudo se mezclan con el código C normal. La falta de sangría de los #defines los hace difíciles de leer. Y la mezcla de código sangrado con #defines sin sangría es una pesadilla.

Una solución común es comentar las directivas, para que sepa fácilmente a qué se refieren:

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

#ifndef FOO
/* a lot of code */
#endif /* not FOO */
Bastien Léonard
fuente
6
He visto ese estilo, mi jefe lo usa. Y, como el resto de su código, es un desastre. Imagine eliminar toda la sangría de sus declaraciones if () normales y usar esos comentarios en su lugar. Se quejará de que no puede ver fácilmente a qué se refieren.
Imán cohete
2

En casi todos los compiladores C / CPP disponibles actualmente no está restringido. Depende del usuario decidir cómo desea alinear el código. Tan feliz codificación.

Hemanth
fuente
1
Respuesta decente. ¿Podrías mejorarlo agregando alguna referencia de guía de estilo específica?
EtherDragon