Допустим, у нас есть кодовая база, которая используется для многих разных клиентов, и у нас есть некоторый код, который важен только для клиентов типа X. Лучше ли использовать директивы препроцессора, чтобы включать этот код только в клиент типа X, или использовать если заявления? Чтобы быть понятнее:
// some code
#if TYPE_X_COSTUMER = 1
// do some things
#endif
// rest of the code
или
if(TYPE_X_COSTUMER) {
// do some things
}
Аргументы, о которых я могу думать:
- Директива препроцессора приводит к меньшему размеру кода и меньшему количеству веток (на неоптимизирующих компиляторах)
- Если в результате утверждения получается код, который всегда компилируется, например, если кто-то допустит ошибку, которая повредит нерелевантный код для проекта, над которым он работает, ошибка все равно появится, и он не повредит кодовую базу. В противном случае он не будет знать о коррупции.
- Мне всегда говорили, что я предпочитаю использовать процессор, а не препроцессор (если это вообще аргумент ...)
Что предпочтительнее - когда речь идет о кодовой базе для разных клиентов?
TYPE_X_CUSTOMER
все еще есть макрос препроцессора?Ответы:
Я думаю, что есть одно преимущество использования #define, которое вы не упомянули, и это тот факт, что вы можете установить значение в командной строке (таким образом, установить его из своего одношагового сценария сборки).
Кроме этого, лучше избегать макросов. Они не уважают какие-либо ограничения, и это может вызвать проблемы. Только очень тупые компиляторы не могут оптимизировать условия, основанные на константе времени компиляции. Я не знаю, является ли это проблемой для вашего продукта (например, может быть важно сохранить небольшой размер кода на встроенных платформах).
источник
Что касается многих вопросов, ответ на этот вопрос зависит . Вместо того, чтобы сказать, что лучше, я привел примеры и цели, где один лучше другого.
И препроцессор, и константа имеют свои собственные места соответствующего использования.
В случае предварительной обработки код удаляется до начала компиляции. Следовательно, он лучше всего подходит для ситуаций, когда ожидается, что код не будет скомпилирован . Это может повлиять на структуру модуля, зависимости и может позволить выбрать лучшие сегменты кода для аспектов производительности. В следующих случаях нужно делить код только по препроцессору.
Многоплатформенный код:
например, когда код компилируется на разных платформах, когда код зависит от конкретных номеров версий ОС (или даже от версии компилятора - хотя это очень редко). Например, когда вы имеете дело с непоследовательными бинарными аналогами кода - они должны быть отделены от препроцессоров, а не констант. Или, если вы компилируете код для Windows, а Linux и некоторые системные вызовы очень разные.
Экспериментальные исправления.
Другим случаем, когда это оправдано, является наличие экспериментального кода, который является рискованным, или определенных основных модулей, которые необходимо исключить, которые будут иметь существенные различия в ссылках или производительности. Причина, по которой можно захотеть отключить код с помощью препроцессора, а не скрывать его под if (), заключается в том, что мы не можем быть уверены в ошибках, внесенных этим конкретным набором изменений, и работаем на экспериментальной основе. Если это не удается, мы должны сделать только одно: отключить этот код в производственной среде, кроме перезаписи. Некоторое время идеально использовать
#if 0
для комментирования всего кода.Работа с зависимостями: еще
одна причина, по которой вам может понадобиться сгенерировать. Например, если вы не хотите поддерживать изображения JPEG, вы можете помочь избавиться от компиляции этого модуля / заглушки, и в конечном итоге библиотека не будет (статически или динамически) связываться с этим модуль. Иногда пакеты запускаются
./configure
для определения доступности такой зависимости, и если библиотеки отсутствуют (или пользователь не хочет их включать), такая функциональность отключается автоматически без привязки к этой библиотеке. Здесь всегда полезно, если эти директивы генерируются автоматически.Лицензирование.
Одним из очень интересных примеров директивы препроцессора является ffmpeg . Он имеет кодеки, которые могут потенциально нарушать патенты при его использовании. Если вы загрузите исходный код и скомпилируете его для установки, он спросит вас, хотите ли вы или удалите такие вещи. Сохраняя коды , спрятанные под некоторыми , если условия по- прежнему могут посадить Вас в суде!
Код копировать-вставить:
ака макросы. Это не совет по чрезмерному использованию макросов - просто у макросов гораздо более эффективный способ применения эквивалента после копирования . Но используйте это с большой осторожностью; и используйте его, если знаете, что делаете. Константы, конечно, не могут этого сделать. Но можно также использовать
inline
функции, если это легко сделать.Так когда вы используете константы?
Почти везде.
Более аккуратный поток кода:
в общем, когда вы используете константы, он почти неотличим от обычных переменных и, следовательно, лучше читаемый код. Если вы пишете подпрограмму, которая состоит из 75 строк, используйте 3 или 4 строки после каждых 10 строк, а #ifdef ОЧЕНЬ не сможет прочитать . Вероятно, с заданной первичной константой, управляемой #ifdef, и использующей ее в любом естественном потоке.
Код с хорошим отступом : все директивы препроцессора, никогда не работают хорошо с другим кодом с хорошим отступом . Даже если ваш компилятор допускает отступ #def, препроцессор Pre-ANSI C не допускал пробела между началом строки и символом "#"; ведущий "#" должен был всегда находиться в первом столбце.
Конфигурация:
Еще одна причина, по которой константы / или переменные имеют смысл, заключается в том, что они могут легко эволюционировать от связи либо с глобальными, либо в будущем могут быть расширены для получения из файлов конфигурации.
И последнее:
никогда не ИСПОЛЬЗУЙТЕ директивы препроцессора
#ifdef
для#endif
пересечения области или{ ... }
. то есть начало#ifdef
или конец#endif
по разные стороны{ ... }
. Это очень плохо; это может сбивать с толку, это может быть иногда опасно.Это, конечно, не исчерпывающий список, но он показывает серьезную разницу в том, какой метод лучше использовать. На самом деле дело не в том, что лучше , а в том, что более естественно использовать в данном контексте.
источник
У ваших клиентов есть доступ к вашему коду? Если да, то препроцессор может быть лучшим вариантом. У нас есть что-то похожее, и мы используем флаги времени компиляции для различных клиентов (или специфических особенностей клиента). Затем скрипт может извлечь код, специфичный для клиента, и мы отправим этот код. Клиенты не знают о других клиентах или других специфических особенностях клиента.
источник
Несколько дополнительных моментов, достойных упоминания:
Компилятор может обрабатывать некоторые виды константных выражений, которые препроцессор не может. Например, препроцессор, как правило, не будет иметь возможности оценивать
sizeof()
не примитивные типы. Это может заставить использованиеif()
в некоторых случаях.Компилятор будет игнорировать большинство вопросов синтаксиса в коде , который пропускается в
#if
(некоторые вопросы , связанные с препроцессором все еще могут вызвать проблемы) , но настаивают на синтаксическую корректность коды пропускаif()
. Таким образом, если код, который пропущен для некоторых сборок, но не для других, становится недействительным вследствие изменений в другом месте файла (например, переименованных идентификаторов), он обычно будет работать на всех сборках, если он отключен,if()
но не отключен, если он отключен#if
. В зависимости от того, почему код пропускается, это может быть или не быть хорошей вещью.Некоторые компиляторы пропускают недоступный код, а другие нет; однако объявления статической длительности хранения в недоступном коде, включая строковые литералы, могут выделять пространство, даже если ни один код не может использовать выделенное таким образом пространство.
Макросы могут использовать
if()
тесты внутри них, но, увы, у препроцессора нет механизма для выполнения какой-либо условной логики в макросе [обратите внимание, что для использования внутри макросов? :
оператор часто может быть лучшеif
, но применяются те же принципы].#if
Директива может использоваться , чтобы контролировать то , что макросы получить определены.Я думаю, что
if()
в большинстве случаев чище, за исключением тех, которые связаны с принятием решений на основе того, какой компилятор используется или какие макросы должны быть определены;#if
однако некоторые из перечисленных выше проблем могут потребовать использования даже в тех случаях, когдаif
они кажутся более чистыми.источник
Короче говоря: страх перед директивами препроцессора в основном 2, множественные включения и, возможно, менее читабельный код и более секретная логика.
Если ваш проект небольшой и практически не имеет большого плана на будущее, я думаю, что оба варианта предлагают одинаковое соотношение плюсов и минусов, но если ваш проект будет огромным, рассмотрите что-то еще, например написание отдельного заголовочного файла, если вы все еще хотите использовать директивы препроцессора, но имейте в виду, что такой подход обычно делает код менее читабельным, и убедитесь, что имя этих констант что-то значит для бизнес-логики самой программы.
источник