Conditional compilation/ru: what changed
Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.
| Old | New | Line | |
|---|---|---|---|
| 1 | 1 | # Что такое условная компиляция? | |
| 2 | 2 | ||
| 3 | 3 | Условная компиляция - это компиляция или пропуск части [[Source code|исходного кода]] в зависимости от того, существует условие или нет. | |
| 4 | 4 | ||
| 5 | 5 | Функции, которые делают это возможным в большинстве компилируемых языков, называются директивами времени компиляции. Директивы времени компиляции позволяют компилировать блок кода на основе наличия или отсутствия условия во [[Compile time|время компиляции]]. Они являются частью [[Compiler directive|директив компилятора]]. | |
| 6 | 6 | ||
| 7 | 7 | Они могут использоваться для различных целей, таких как: | |
| 8 | 8 | ||
| 9 | 9 | - изоляция кода для конкретной платформы | |
| 10 | 10 | - выбор естественного языка | |
| 11 | 11 | - лицензирование частей с открытым и закрытым исходным кодом | |
| 12 | 12 | - изоляция экспериментального кода | |
| 13 | 13 | - версия [[Compiler|компилятора]] | |
| 14 | 14 | - версия библиотеки | |
| 15 | 15 | - и т.д. и т.п. | |
| 16 | 16 | ||
| 17 | 17 | [[FPC/ru|Free Pascal]] поддерживает четыре различных стиля условной компиляции: | |
| 18 | 18 | ||
| 19 | 19 | - [[Turbo Pascal]] и ранние директивы стиля [[Delphi]] | |
| 20 | 20 | - директивы стиля [[Mac Pascal]] | |
| 21 | 21 | - Современные директивы в стиле Free Pascal и Delphi | |
| 22 | 22 | - Макросы времени компиляции | |
| 23 | 23 | ||
| 24 | 24 | > **Note:** здесь синтаксис не [[case-sensitive|чувствителен к регистру]], поскольку соответствует всему синтаксису [[Pascal]]. Мы будем использовать как строчные, так и прописные примеры. Мы покажем вам разницу между режимами и как их эффективно использовать. | |
| 25 | 25 | ||
| 26 | 26 | # Директивы в стиле Turbo Pascal | |
| 27 | 27 | ||
| 28 | 28 | Директивами в стиле Turbo Pascal являются `{$DEFINE}`, `{$IFDEF}`, `{$ENDIF}`, `{$IFNDEF}`, `{$IFOPT}`, `{$ELSE}`, `{$ELSEIF}` и `{$UNDEF}`. | |
| 29 | 29 | ||
| 30 | 30 | Мы опишем директивы в контексте стиля. Некоторые определения имеют расширенное значение в другом стиле. | |
| 31 | 31 | ||
| 32 | 32 | Это означает, что в дальнейшем мы можем расширить значение некоторых директив, таких как, например, `{$DEFINE}`, в контексте макросов. | |
| 33 | 33 | ||
| 34 | 34 | ## \$define | |
| 35 | 35 | ||
| 36 | 36 | Директива `{$DEFINE}` просто объявляет символ, который мы позже можем использовать для условной компиляции: | |
| 37 | 37 | ||
| 38 | 38 | ``` pascal | |
| 39 | 39 | {$DEFINE name} //Это объявляет символ под названием "name" | |
| 40 | 40 | ``` | |
| 41 | 41 | ||
| 42 | 42 | Обратите внимание, что вы также можете объявить символ из [[Command-line interface|командной строки]] или , например, -dDEBUG [[IDE]], который будет являться эквивалентом командной строки | |
| 43 | 43 | ||
| 44 | 44 | ``` pascal | |
| 45 | 45 | {$DEFINE DEBUG} | |
| 46 | 46 | ``` | |
| 47 | 47 | ||
| 48 | 48 | в исходном коде. | |
| 49 | 49 | ||
| 50 | 50 | ## \$undef | |
| 51 | 51 | ||
| 52 | 52 | Директива `{$UNDEF}` отменяет объявление ранее определенного символа. Вот пример, который автор использует на практике: | |
| 53 | 53 | ||
| 54 | 54 | ``` pascal | |
| 55 | 55 | // Некоторый старый исходный код, "загрязненый" обилием {$IFDEF FPC}, который больше | |
| 56 | 56 | // не нуждается в зависимости от версии Delphi, с которой он должен быть совместим. | |
| 57 | 57 | // Я всегда проверяю это, пробуя это поверх программы или модуля: | |
| 58 | 58 | {$IFDEF FPC} | |
| 59 | 59 | {$MODE DELPHI} | |
| 60 | 60 | {$UNDEF FPC} | |
| 61 | 61 | {$DEFINE VER150} | |
| 62 | 62 | // код теперь будет компилироваться так, как если бы это был Delphi 7, при условии, что исходный код Delphi действительно был написан для Delphi 7 и выше. | |
| 63 | 63 | {$ENDIF} | |
| 64 | 64 | ``` | |
| 65 | 65 | ||
| 66 | 66 | ## \$ifdef и \$endif | |
| 67 | 67 | ||
| 68 | 68 | Самый простой способ объявить блок условного кода так: | |
| 69 | 69 | ||
| 70 | 70 | ``` pascal | |
| 71 | 71 | unit cross; | |
| 72 | 72 | {$IFDEF FPC}{$MODE DELPHI}{$ENDIF} | |
| 73 | 73 | ``` | |
| 74 | 74 | ||
| 75 | 75 | Приведенный выше пример довольно распространен для исходного кода, который должен компилироваться как в Delphi, так и в Free Pascal. | |
| 76 | 76 | ||
| 77 | 77 | Если компилятором является Delphi, то ничего не делается, но если компилятором является Free Pascal, он переключит Free Pascal для компиляции и использования режима синтаксиса Delphi. | |
| 78 | 78 | ||
| 79 | 79 | Этот условный символ "FPC" объявлен в системе - там их длинный список. Синтаксис блоков `{$IFDEF}` и `{$ENDIF}` является симметричным: каждый `{$IFDEF}` имеет свой собственный `{$ENDIF}`. | |
| 80 | 80 | ||
| 81 | 81 | Чтобы помочь вам распознать соответствующие блоки, вы можете использовать, например, отступ, но вы также можете использовать функцию [[Comments|комментариев]]: | |
| 82 | 82 | ||
| 83 | 83 | ``` pascal | |
| 84 | 84 | {$IFDEF FPC эта часть специфична для Free Pascal} | |
| 85 | 85 | // некий Free Pascal специфичный код | |
| 86 | 86 | {$ENDIF Free Pascal специфичный код} | |
| 87 | 87 | ``` | |
| 88 | 88 | ||
| 89 | 89 | > **Warning:** Эта особенность комментариев часто не совсем понятна. Некоторые люди - как и в более старой версии этой записи вики - предположили, что вы можете вкладывать `{$IFDEF}`, потому что компилятор, кажется, принимает синтаксис. Но первое неверно, а второе верно: да, компилятор принимает приведенный ниже синтаксис, но это не вложенное `{$IFDEF}`, а одиночное условие `{$IFDEF}`, а остальное - комментарий! | |
| 90 | 90 | > | |
| 91 | 91 | > Приведенный ниже код выполнит writeln тогда и только тогда, когда будет объявлен `{$define red}`. В этом примере `{$ifdef blue}` является комментарием! Даже если `{$define blue}` допустим. | |
| 92 | 92 | ||
| 93 | 93 | ``` pascal | |
| 94 | 94 | // программа формально будет скомпилирована, но полностью неработоспособна | |
| 95 | 95 | {$define blue} | |
| 96 | 96 | begin | |
| 97 | 97 | {$ifdef red or $ifdef blue}// все после red - это комментарий | |
| 98 | 98 | writeln ('red or blue'); // этот код никогда не выполнится | |
| 99 | 99 | {$endif red or blue} // все, что объявлено после $endif, является комментарием. | |
| 100 | 100 | end. | |
| 101 | 101 | ``` | |
| 102 | 102 | ||
| 103 | 103 | ## \$ifndef | |
| 104 | 104 | ||
| 105 | 105 | Это противоположно `{$IFDEF}`, и ниже лежащий код выполнится, если объявленное условие не выполняется. Простой пример: | |
| 106 | 106 | ||
| 107 | 107 | ``` pascal | |
| 108 | 108 | {$IFNDEF FPC эта часть не для Free Pascal} | |
| 109 | 109 | // некий конкретный код, который Free Pascal не должен компилировать | |
| 110 | 110 | {$ENDIF код для других компиляторов, кроме Free Pascal} | |
| 111 | 111 | ``` | |
| 112 | 112 | ||
| 113 | 113 | ## \$else и \$elseif | |
| 114 | 114 | ||
| 115 | 115 | `{$ELSE}` используется для компиляции кода, который не принадлежит блоку кода, который объявлен соответствующим `{$IFDEF}`. Он также действителен в контексте `{$IFOPT}`, `{$IF}` или `{$IFC}`, которые мы обсудим позже. | |
| 116 | 116 | ||
| 117 | 117 | ``` pascal | |
| 118 | 118 | {$IFDEF red} | |
| 119 | 119 | writeln('Red объявлен'); | |
| 120 | 120 | {$ELSE no red} | |
| 121 | 121 | {$IFDEF blue} | |
| 122 | 122 | writeln('Blue объявлен, а red - не объявлен'); | |
| 123 | 123 | {$ELSE no blue} | |
| 124 | 124 | writeln('Ни red, ни blue не объявлены'); | |
| 125 | 125 | {$ENDIF blue} | |
| 126 | 126 | {$ENDIF red} | |
| 127 | 127 | ``` | |
| 128 | 128 | ||
| 129 | 129 | Такие вложенные условные выражения, написанные в приведенном выше синтаксисе, могут стать очень путанными и нечитаемыми. К счастью, мы можем упростить это, используя `{$ELSEIF}`. Код ниже является расширенным эквивалентом первого примера: | |
| 130 | 130 | ||
| 131 | 131 | ``` pascal | |
| 132 | 132 | {$IF Defined(red)} | |
| 133 | 133 | writeln('Red объявлен'); | |
| 134 | 134 | {$ELSEIF Defined(blue)} | |
| 135 | 135 | writeln('Blue объявлен'); | |
| 136 | 136 | {$ELSEIF Defined(green)} | |
| 137 | 137 | writeln('Green объявлен'); | |
| 138 | 138 | {$ELSE} | |
| 139 | 139 | writeln('Ни red, ни blue, ни green не объявлены. Должен быть black...или что-нибудь еще...'); | |
| 140 | 140 | {$ENDIF} | |
| 141 | 141 | ``` | |
| 142 | 142 | ||
| 143 | 143 | Как вы можете видеть, это гораздо более читабельно. | |
| 144 | 144 | ||
| 145 | 145 | ## \$ifopt | |
| 146 | 146 | ||
| 147 | 147 | С помощью `{$IFOPT}` мы можем проверить, задана ли определенная опция компиляции. | |
| 148 | 148 | ||
| 149 | 149 | Из руководства по программированию: | |
| 150 | 150 | ||
| 151 | - | ||
| 152 | - | ||
| 153 | - | ||
| 151 | + | ```text | |
| 152 | + | {$IFOPT switch} скомпилирует текст, который следует за ним, если переключатель switch | |
| 153 | + | в данный момент будет находиться в указанном состоянии. Если он не находится в указанном состоянии, | |
| 154 | + | то компиляция продолжится после соответствующей директивы {$ELSE} или {$ENDIF}. | |
| 155 | + | ``` | |
| 154 | 156 | ||
| 155 | 157 | Например: | |
| 156 | 158 | ||
| 157 | 159 | ``` pascal | |
| 158 | 160 | {$IFOPT M+} | |
| 159 | 161 | Writeln('Compiled with type information'); | |
| 160 | 162 | {$ENDIF} | |
| 161 | 163 | ``` | |
| 162 | 164 | ||
| 163 | 165 | Скомпилирует оператор Writeln, только если включена генерация информации о типе. | |
| 164 | 166 | ||
| 165 | 167 | > **Note:** директива `{$IFOPT}` принимает только короткие опции, т.е. `{$IFOPT TYPEINFO}` не будет принята. | |
| 166 | 168 | ||
| 167 | 169 | Обычно этот пример используется для проверки, задан ли режим DEBUG: | |
| 168 | 170 | ||
| 169 | 171 | ``` pascal | |
| 170 | 172 | {$IFOPT D+}{$NOTE debug mode is active}{$ENDIF} | |
| 171 | 173 | ``` | |
| 172 | 174 | ||
| 173 | 175 | Такие определения также могут находиться в конфигурационных файлах, таких как fpc.cfg, которые также содержат полное объяснение того, как использовать: | |
| 174 | 176 | ||
| 175 | - | ||
| 176 | - | ||
| 177 | - | ||
| 178 | - | ||
| 179 | - | ||
| 180 | - | ||
| 181 | - | ||
| 182 | - | ||
| 183 | - | ||
| 184 | - | ||
| 185 | - | ||
| 186 | - | ||
| 187 | - | ||
| 188 | - | ||
| 189 | - | ||
| 190 | - | ||
| 191 | - | ||
| 192 | - | ||
| 193 | - | ||
| 194 | - | ||
| 177 | + | ```text | |
| 178 | + | # ---------------------- | |
| 179 | + | # Defines (preprocessor) | |
| 180 | + | # ---------------------- | |
| 181 | + | # | |
| 182 | + | # nested #IFNDEF, #IFDEF, #ENDIF, #ELSE, #DEFINE, #UNDEF are allowed | |
| 183 | + | # | |
| 184 | + | # -d is the same as #DEFINE | |
| 185 | + | # -u is the same as #UNDEF | |
| 186 | + | # | |
| 187 | + | # | |
| 188 | + | # Some examples (for switches see below, and the -? help pages) | |
| 189 | + | # | |
| 190 | + | # Try compiling with the -dRELEASE or -dDEBUG on the command line | |
| 191 | + | # | |
| 192 | + | # For a release compile with optimizes and strip debug info | |
| 193 | + | #IFDEF RELEASE | |
| 194 | + | -O2 | |
| 195 | + | -Xs | |
| 196 | + | #WRITE Compiling Release Version | |
| 197 | + | #ENDIF | |
| 198 | + | ``` | |
| 195 | 199 | ||
| 196 | 200 | # Как не нужно делать | |
| 197 | 201 | ||
| 198 | 202 | Что не так с этим кодом? Вы можете это заметить? | |
| 199 | 203 | ||
| 200 | 204 | ``` pascal | |
| 201 | 205 | var | |
| 202 | 206 | MyFilesize: | |
| 203 | 207 | {$ifdef Win32} | |
| 204 | 208 | Cardinal | |
| 205 | 209 | {$else} | |
| 206 | 210 | int64 | |
| 207 | 211 | {$endif} | |
| 208 | 212 | ``` | |
| 209 | 213 | ||
| 210 | 214 | Вот ответ: | |
| 211 | 215 | ||
| 212 | 216 | - Free Pascal компилируется для большего количества типов процессоров, чем [[32 bit|32]] и [[64 bit]], например, для 8 и 16 бит. | |
| 213 | 217 | - на большинстве 64-битных платформ максимальный размер файла - это [[QWord]], а не [[Int64]]. | |
| 214 | 218 | ||
| 215 | 219 | Этот программист попал в ловушку, которая распространена: если вы используете объявление, убедитесь, что ваша логика надежна. В противном случае такой код может легко вызвать несчастные случаи. Компилятор не будет ловить ваши логические ошибки! | |
| 216 | 220 | ||
| 217 | 221 | Всегда хорошо осознавать такие вещи, тем более что такие вещи легко исправить. | |
| 218 | 222 | ||
| 219 | 223 | ``` pascal | |
| 220 | 224 | var | |
| 221 | 225 | MyFilesize: | |
| 222 | 226 | {$if defined(Win32)} | |
| 223 | 227 | Cardinal | |
| 224 | 228 | {$elseif defined(Win64)} | |
| 225 | 229 | Qword; | |
| 226 | 230 | {$else} | |
| 227 | 231 | {$error этот код написан для win32 или win64} | |
| 228 | 232 | {$endif} | |
| 229 | 233 | ``` | |
| 230 | 234 | ||
| 231 | 235 | Помимо этого, есть решение для этого конкретного примера, которое вообще не использует условные выражения: | |
| 232 | 236 | ||
| 233 | 237 | ``` pascal | |
| 234 | 238 | var | |
| 235 | 239 | MyFilesize:NativeUint; | |
| 236 | 240 | ``` | |
| 237 | 241 | ||
| 238 | 242 | Что не так с этим кодом? Вы можете это заметить? | |
| 239 | 243 | ||
| 240 | 244 | ``` pascal | |
| 241 | 245 | ||
| 242 | 246 | {$IFDEF BLUE AND $IFDEF RED} Form1.Color := clYellow; {$ENDIF} | |
| 243 | 247 | {$IFNDEF RED AND $IFNDEF BLUE} Form1.Color := clAqua; {$ENDIF} | |
| 244 | 248 | ``` | |
| 245 | 249 | ||
| 246 | 250 | Вот ответ: | |
| 247 | 251 | ||
| 248 | 252 | - Ну, я уже написал комментарий, который предупредил вас .. так что посмотрите на предупреждение .... Вы должны быть в состоянии определить это ... | |
| 249 | 253 | - Директивы компилятора переопределяют компилятор ... будьте осторожны с этим [топором Юджина](https://ru.wikipedia.org/wiki/Careful_with_That_Axe,_Eugene). | |
| 250 | 254 | ||
| 251 | 255 | --- | |
| 252 | 256 | *From the Free Pascal wiki, page "Conditional compilation/ru", as it stood on 2022-08-06 after a change by Kai Burghardt, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* |
