Sangrado #defines

Sé que #define s, etc. normalmente nunca se sangran. ¿Por qué?

Estoy trabajando en algún código en el momento que tiene una mezcla horrible de #define s, #ifdef s, #else s, #endif s, etc. Todos estos a menudo se mezclan con el código C normal. La falta de sangría de #define hace que sea difícil de leer. Y la mezcla de código sangrado con #define sin sangrado es una pesadilla.

¿Cuál es el beneficio de no sangrar #define s? ¿Me convierte en una mala persona si los sangro? ¿No es esto mucho más agradable?

 #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 

Preprocesador Pre-ANSI C no permitió espacio entre el inicio de una línea y el carácter “#”; el “#” inicial siempre debe colocarse en la primera columna.

Los comstackdores anteriores a ANSI C son inexistentes 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

Como algunos ya han dicho, algunos comstackdores Pre-ANSI requieren que # sea el primer carácter en la línea pero no requieren que se adjunte una directiva de 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 el color de la syntax 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 nivel meta, por lo que no deberían formar parte del flujo normal de código). Incluso puede ver que SO no colorea la secuencia de manera útil.

Con respecto al análisis sintáctico de las directivas de preprocesador, el estándar C99 (y el estándar C89 anterior) eran claras sobre la secuencia de operaciones realizadas lógicamente por el comstackdor. En particular, creo que significa que este código:

 /* */ # /* */ include /* */  /* */ 

es equivalente a:

 #include  

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

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

  1. [Correlación de caracteres, incluidos los trigrafos]

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

  3. El archivo fuente se descompone en tokens de preprocesamiento y secuencias de caracteres en espacios en blanco (incluidos los comentarios). Un archivo de origen no debe terminar en un token de preprocesamiento parcial o en un comentario parcial. Cada comentario es reemplazado por un carácter de espacio. Los caracteres de nueva línea se conservan. Si cada secuencia no vacía de caracteres de espacios en blanco que no sean de nueva línea es retenida o reemplazada por un carácter de espacio está definida por la implementación.

  4. Las directivas de preprocesamiento se ejecutan, las macrovocaciones se expanden, […]

Las directivas de preprocesamiento de la sección 6.10 dicen:

Una directiva de preprocesamiento consiste en una secuencia de tokens de preprocesamiento que comienza con un token de preprocesamiento # que (al inicio 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 sigue el espacio en blanco que contiene al menos un carácter de nueva línea, y termina con el siguiente carácter de nueva línea.

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

Como han señalado otros, los preprocesadores C preestandarizados no se comportaban de manera uniforme de varias maneras, y los espacios antes y en las directivas de preprocesador era una de las áreas donde los diferentes comstackdores hacían cosas diferentes, incluido el no reconocer las directivas de preprocesador con espacios por delante .

Es de destacar que la eliminación de barra invertida-nueva línea se produce antes de analizar los comentarios. En consecuencia, no debe finalizar // comentarios con una barra diagonal inversa.

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

Una cosa que se interpone en mi camino (y algunas veces me convence de que deje de intentarlo) es que muchos o la mayoría de los editores / IDE lanzarán la directiva a la columna 1 a la menor provocación. Lo cual es molesto como el infierno.

En estos días, creo que esto es principalmente una elección de estilo. Creo que en un momento dado en el pasado distante, no todos los comstackdores soportaron la noción de sangrado que el preprocesador define. Investigué un poco y no pude respaldar esa afirmación. Pero en cualquier caso, parece que todos los comstackdores modernos admiten la idea de sangrar el macro del preprocesador. No tengo una copia del estándar C o C ++, así que no sé si esto es un comportamiento estándar o no.

En cuanto a si es un buen estilo o no. Personalmente, me gusta la idea de mantenerlos a todos a la izquierda. Te da un lugar consistente para buscarlos. Sí, puede ser molesto cuando hay macros muy nesteds. Pero si los sangra, eventualmente terminará con un código que parece aún más extraño.

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

Para el ejemplo que le ha dado, puede ser apropiado usar una 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 rest de tu código. Directivas como #ifdef son manejadas por el preprocesador, antes de que el comstackdor alguna vez vea tu 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 separadas visualmente del rest de su código es más importante cuando están intercaladas con el código (en lugar de un bloque de directivas dedicadas, como en el ejemplo que usted brinda).

Estoy trabajando en algún 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 #defines hace que sea difícil 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 */