Отступы #defines

Я знаю, что #define s и т.д. обычно никогда не отступаются. Почему?

В настоящий момент я работаю над некоторым кодом, который имеет ужасную смесь #define s, #ifdef s, #else s, #endif s и т.д. Все это часто смешивается с обычным кодом C, Отступ в #define делает их трудными для чтения. И смесь с отступом с не с отступом #define - это кошмар.

В чем преимущество отступов #define s? Это делает меня плохим человеком, если я их отступлю? Разве это не намного лучше?

#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

Ответ 1

Препроцессор pre-ANSI C не допускал пробела между началом строки и символом "#"; ведущий "#" всегда должен быть помещен в первую колонку.

Компиляторы Pre-ANSI C в наши дни отсутствуют. Используйте любой стиль (пробел перед "#" или пробел между "#" и идентификатором).

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

Ответ 2

Как уже говорилось, некоторые компиляторы AN-ANSI должны были # быть первым char в строке, но они не требовали, чтобы директива препроцессора была привязана к ней, поэтому отступ был сделан таким образом.

#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

Я часто видел этот стиль в старых заголовках Unix, но я ненавижу его, поскольку раскраска синтаксиса часто не работает на таком коде. Я использую очень видимый цвет для предпроцессорной директивы, чтобы они выделялись (они находятся на мета уровне, поэтому не должны быть частью нормального потока кода). Вы даже можете видеть, что SO не окрашивает последовательность полезным образом.

Ответ 3

Что касается анализа директив препроцессора, то стандарт C99 (и стандарт C89 перед ним) был ясен о последовательности операций, выполняемых логически компилятором. В частности, я считаю, что это означает, что этот код:

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

эквивалентно:

#include <stdio.h>

К лучшему или худшему, GCC 3.4.4 с '-std = c89 -pedantic', во всяком случае, согласен с комментариями. Я не защищаю это как стиль - не на секунду (это ужасно). Я просто думаю, что это возможно.

ISO/IEC 9899: 1999 раздел 5.1.1.2. Фазы перевода говорят:

  • [Отображение символов, включая триграфы]

  • [Линейное сращивание - удаление обратной косой черты)

  • Исходный файл разбивается на токены предварительной обработки и последовательности символы пробела (включая комментарии). Исходный файл не должен заканчиваться частичный токен предварительной обработки или частичный комментарий. Каждый комментарий заменяется на один пробел. Символы новой строки сохраняются. Независимо от того, последовательность символов пробела, отличных от новой строки, сохраняется или заменяется на один пробельный символ определяется реализацией.

  • Выполняются предпроцессорные директивы, расширяются макрокоманды, [...]

Раздел 6.10. Директивы предварительной обработки говорят:

Директива предварительной обработки состоит из последовательности токенов предварительной обработки, которая начинается с токен предварительной обработки, который (в начале фазы 4 перевода) является либо первым символом в исходном файле (необязательно после пробела, не содержащего символов новой строки) или следует пробел, содержащий хотя бы один символ новой строки, и заканчивается следующим символ новой строки.

Единственный возможный спор - это выражение в скобках "(в начале фазы перевода 4)", что может означать, что комментарии перед хешем должны отсутствовать, поскольку они не заменяются иначе пробелами до конца фазы 4.

Как отмечали другие, пре-стандартные препроцессоры C не вели себя одинаково разными способами, а пробелы до и в предпроцессорных директивах были одной из областей, где разные компиляторы делали разные вещи, в том числе не распознавая директивы препроцессора с пробелами перед ними.

Следует отметить, что удаление обратной косой черты происходит до того, как будут проанализированы комментарии. Следовательно, вы не должны заканчивать комментарии // обратной косой чертой.

Ответ 4

Я не знаю, почему это не чаще. Есть, конечно, времена, когда мне нравятся директивы препроцессора отступа.

Одна вещь, которая мешает мне (и иногда убеждает меня прекратить попытки), состоит в том, что многие или большинство редакторов /IDE будут бросать директиву в столбец 1 при малейшей провокации. Что раздражает, как ад.

Ответ 5

В эти дни я считаю, что это в основном выбор стиля. я думаю в какой-то момент в далеком прошлом, не все компиляторы поддерживают понятие отступающего препроцессора. Я сделал некоторые исследования и не смог поддержать это утверждение. Но в любом случае, похоже, что все современные компиляторы поддерживают идею отступать от предварительного процессора. У меня нет копии стандарта C или С++, хотя я не знаю, является ли это стандартным поведением или нет.

Что касается хорошего стиля. Лично мне нравится идея держать их всех слева. Это дает вам постоянное место для поиска. Да, это может раздражать, когда есть очень вложенные макросы. Но если вы отступите от них, вы в конечном итоге получите еще более странный код.

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

Ответ 6

Для примера, который вы указали, может быть целесообразно использовать отступ, чтобы сделать его более понятным, поскольку у вас есть такая сложная структура вложенных директив.

Лично я считаю, что полезно держать их не с отступом большую часть времени, потому что эти директивы действуют отдельно от остальной части вашего кода. Директивы, такие как #ifdef, обрабатываются предварительным процессором, прежде чем компилятор когда-либо увидит ваш код, поэтому блок кода после директивы #ifdef может даже не компилироваться.

Сохранение директив, визуально отделенных от остальной части вашего кода, более важно, когда они перемежаются с кодом (а не выделенным блоком директив, как в примере, который вы даете).

Ответ 7

В настоящий момент я работаю над некоторым кодом, который имеет ужасную смесь #defines, #ifdefs, #elses, #endifs, #etc. Все это часто смешивается с обычным кодом C. Отступы в #defines затрудняют их чтение. И смесь с отступом кода с не отступами #defines - это кошмар.

Общим решением является комментировать директивы, так что вы легко знаете, к чему они относятся:

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

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