Дос и Донны условной компиляции
Когда делает условную компиляцию хорошей идеей и когда это ужасно плохая идея?
Посредством условного компиляции я имею в виду использование #ifdef
для компиляции определенных битов кода в определенных условиях. Сами #defined
могут быть либо в общем файле заголовка, либо через директиву -D
компилятора.
Ответы
Ответ 1
Хорошие идеи:
- защита заголовков (вы не можете сделать намного лучше для переносимости)
- условная реализация (жонглирование различиями в платформе)
- отладить специальные проверки (утверждает и т.д.)
- за предложение:
extern "C" {
и }
, чтобы одни и те же заголовки могли использоваться реализацией С++ и клиентами C API
Плохая идея:
- изменение API между флагами компиляции, поскольку это заставляет клиента изменять его использование с теми же флагами компиляции... urk!
Ответ 2
В принципе, вы должны попытаться сохранить минимальный код, который условно скомпилирован, потому что вы должны пытаться проверить все это и иметь множество условий, что усложняет работу. Это также снижает читаемость кода; условная компиляция целых файлов более ясна, например, путем размещения кода, специфичного для конкретной платформы, в отдельном файле для каждой платформы и с тем, чтобы все они имели один и тот же API с точки зрения остальной части проблемы. Также старайтесь избегать использования его в заголовках функций; опять же, потому что это место, где это особенно запутывает.
Но это не означает, что вы никогда не должны использовать условную компиляцию. Просто старайтесь держать его коротким и минимальным. (Где я могу, я использую условную компиляцию для управления определениями других макросов, которые тогда просто используются в остальной части кода, что, по-видимому, для меня яснее.)
Ответ 3
Не помещайте ifdef в свой код.
Это очень трудно читать и понимать. Пожалуйста, сделайте код таким же простым для понимания, насколько это возможно для сопровождающего (он знает, где вы живете и владеете топором).
Скрыть условный код в отдельных функциях и использовать ifdef для определения того, какие функции используются.
DONT используйте часть else для определения. Если вы говорите, что вы говорите, что одна платформа уникальна, а все остальные одинаковы. Это маловероятно, более вероятно, что вы знаете, что происходит на нескольких платформах, но вы должны использовать раздел #else для привязки #error, поэтому, когда он переносится на новую платформу, разработчику необходимо явно установить условие для его платформу.
x.h
#if defined(WINDOWS)
#define MyPlatfromSleepSeconds(x) sleep(x * 1000)
#elif defined (UNIX)
#define MyPlatfromSleepSeconds(x) Sleep(x)
#else
#error "Please define appropriate sleep for your platform"
#endif
Не пытайтесь развернуть макрос на несколько строк кода. Это приводит к безумию.
p.h
#if defined(SOLARIS_3_1_1)
#define DO_SOME_TASK(x,y) doPartA(x); \
doPartB(y); \
couple(x,y)
#elif defined(WINDOWS)
#define DO_SOME_TASK(x,y) doAndCouple(x,y)
#else
#error "Please define appropriate DO_SOME_TASK for your platform"
#endif
Если вы разрабатываете код на окнах, а затем тестируете на solaris 3_1_1, вы можете обнаружить неожиданные ошибки, когда люди делают такие вещи, как:
int loop;
for(loop = 0;loop < 10;++loop)
DO_SOME_TASK(loop,loop); // Windows works fine()
// Solaras. Only doPartA() is in the loop.
// The other statements are done when the loop finishes
Ответ 4
Это плохая идея, когда вы не знаете, что делаете. Это может быть хорошей идеей, когда вы решаете проблему таким образом:).
То, как вы описываете условную компиляцию, включает в себя охранники, является ее частью. Это не только хорошая идея. Это способ избежать ошибок компиляции.
Для меня условная компиляция также является способом нацеливания нескольких компиляторов и операционных систем. Я участвую в lib, который должен быть скомпилирован в Windows XP и новее, 32 или 64 бит, используя MinGW и Visual С++, на Linux 32 и 64 бит, используя gcc/g++ и на MacOS, используя I-don't-know -что (я не поддерживаю это, но я предполагаю, что это gcc-порт). Без условий препроцессора было бы практически невозможно создать один исходный файл, который можно скомпилировать в любом месте.
Ответ 5
Другим прагматичным использованием условных компиляций является "прокомментировать" разделы кода, содержащие стандартные комментарии "С" (т.е./* */). Некоторые компиляторы не допускают вложения этих комментариев, например:
/* comment out block of code
.... code ....
/* This is a standard
* comment.
*/ ... oopos! Some compilers try to compile code after this closing comment.
.... code ....
end of block of code*/
(Как вы можете видеть в подсветке синтаксиса, StackOverflow не вставляет комментарии.)
вместо этого вы можете использовать #ifdef
для получения правильного эффекта, например:
#ifdef _NOT_DEFINED_
.... code ....
/* This is a standard
* comment.
*/
.... code ....
#endif
Ответ 6
В прошлом, если вы хотели создать действительно портативный код, вам придется прибегнуть к какой-то форме условной компиляции. При наличии распространения портативных библиотек (таких как APR, boost и т.д.) Эта причина имеет небольшой вес IMHO. Если вы используете условную компиляцию, просто скомпилируйте блоки кода, которые не нужны для конкретных сборок, вы должны действительно пересмотреть свой дизайн - я должен представить, что это станет кошмаром для поддержания.
Сказав все это, если вам нужно использовать условную компиляцию, я бы спрятал столько, сколько мог, от основной части кода и ограничился очень конкретными случаями, которые очень хорошо поняты.
Ответ 7
Хорошее/обоснованное использование основано на анализе затрат и результатов. Очевидно, что люди здесь очень осознают риски:
- при связывании объектов, которые видели разные версии классов, функций и т.д.
- при сложном понимании кода, проверке и причине о
Но есть применения, которые часто попадают в категорию нетто-пользы:
- защита заголовков
- для отдельных программных "экосистем", таких как Linux в сравнении с Windows, Visual С++ и GCC, оптимизация по конкретным процессорам, иногда размер слов и коэффициенты суждения (хотя с С++ вы часто можете определить их при компиляции с помощью хакерства шаблонов, но что может оказаться более беспорядочным) - абстрагирует различия на более низком уровне, чтобы обеспечить согласованный API в этих средах.
- взаимодействие с существующим кодом, использующим препроцессор, определяет выбор версий API, стандартов, поведения, безопасности потоков, протоколов и т.д. (грустно, но верно).
- которая может использовать дополнительные функции, когда они доступны (подумайте о сценариях GNU configure и всех тестах, которые они выполняют на интерфейсах ОС и т.д.)
- запросить создание дополнительного кода в модуле перевода, например добавление
main()
для автономного приложения или без библиотеки
- контроль включения кода для различных режимов логической сборки, таких как debug и release
Ответ 8
Это всегда плохая идея. То, что он делает, эффективно создает несколько версий исходного кода, все из которых необходимо протестировать, что является, по меньшей мере, болью. К сожалению, как и многие плохие вещи, это иногда неизбежно. Я использую его в очень небольших количествах при написании кода, который нужно портировать между Windows и Linux, но если бы я сам это делал, я бы рассмотрел альтернативы, например, наличие двух отдельных поддеревьев разработки.