Отступы #defines

99

Я знаю, что #defines и т. Д. Обычно не имеют отступа. Зачем?

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

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

#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
Ракетмагнит
источник

Ответы:

103

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

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

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


источник
26

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

#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 не окрашивает последовательность полезным способом.

Патрик Шлютер
источник
16

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

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

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

#include <stdio.h>

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

В разделе 5.1.1.2 «Фазы перевода» ИСО / МЭК 9899: 1999 говорится:

  1. [Сопоставление символов, включая триграфы]

  2. [Соединение строк - удаление новой строки с обратной косой чертой]

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

  4. Выполняются директивы предварительной обработки, расширяются вызовы макросов, [...]

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

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

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

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

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

Джонатан Леффлер
источник
7

Не знаю, почему это не так часто. Конечно, бывают случаи, когда мне нравится делать отступы в директивах препроцессора.

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

Майкл Берр
источник
5

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

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

#if COND1
void foo() {
  #if COND2
  int i;
    #if COND3
  i = someFunction()
  cout << i << eol;
    #endif
  #endif
}
#endif
ДжаредПар
источник
14
Причина, по которой этот код выглядит странно, заключается в том, что вы создали два «потока» отступов. Я бы сделал отступ для строки 4 еще на один уровень, а строки 6 и 7 - еще на два уровня.
Кевин Лэйти,
3
Полностью согласен. Иногда я даже ставлю скобки, чтобы # if выглядели так же, как если бы.
baash05,
3
Я очень стараюсь организовать свой код так, чтобы в нем не было #ifdef строк в тех частях, где у меня есть реальный код. Вместо этого, если мне нужны условные вещи, я либо помещаю их в факторизованные функции, либо извлекаю макросы; так я нахожу намного яснее (ну, по крайней мере, для меня). В идеале все эти разложенные части будут в других файлах (заголовках или условно скомпилированных исходных файлах; обычным «условием» является то, для какой платформы создается код).
Donal Fellows
2
Я бы сделал отступ для строк 4 на один уровень, а для строк 6 и 7 - на два уровня.
Rocketmagnet
3

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

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

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

Даниэль Фортунов
источник
3
С точки зрения IP, в чем разница между тем, что не скомпилировано, и тем, чего не удалось достичь из-за jmp.
baash05,
2

В данный момент я работаю над кодом, который содержит ужасную смесь #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 */
Бастьен Леонар
источник
6
Я видел этот стиль, мой босс его использует. И, как и остальная часть его кода, это просто беспорядок. Представьте, что вы удалили все отступы из обычных операторов if () и вместо этого использовали эти комментарии. Вы будете жаловаться, что не можете легко понять, о чем они говорят.
Rocketmagnet
2

Практически во всех доступных в настоящее время компиляторах C / CPP это не ограничено. Пользователь сам решает, как вы хотите выровнять код. Так счастливого кодирования.

Hemanth
источник
1
Достойный ответ. Не могли бы вы улучшить его, добавив какой-нибудь конкретный справочник по стилю?
EtherDragon