Sign in Join
Wiki copytemporary

Conditional compilation/ru: what changed

Revision 2 to 3. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11# Что такое условная компиляция?
22
33Условная компиляция - это компиляция или пропуск части [[Source code|исходного кода]] в зависимости от того, существует условие или нет.
44
55Функции, которые делают это возможным в большинстве компилируемых языков, называются директивами времени компиляции. Директивы времени компиляции позволяют компилировать блок кода на основе наличия или отсутствия условия во [[Compile time|время компиляции]]. Они являются частью [[Compiler directive|директив компилятора]].
66
77Они могут использоваться для различных целей, таких как:
88
99- изоляция кода для конкретной платформы
1010- выбор естественного языка
1111- лицензирование частей с открытым и закрытым исходным кодом
1212- изоляция экспериментального кода
1313- версия [[Compiler|компилятора]]
1414- версия библиотеки
1515- и т.д. и т.п.
1616
1717[[FPC/ru|Free Pascal]] поддерживает четыре различных стиля условной компиляции:
1818
1919- [[Turbo Pascal]] и ранние директивы стиля [[Delphi]]
2020- директивы стиля [[Mac Pascal]]
2121- Современные директивы в стиле Free Pascal и Delphi
2222- Макросы времени компиляции
2323
2424> **Note:** здесь синтаксис не [[case-sensitive|чувствителен к регистру]], поскольку соответствует всему синтаксису [[Pascal]]. Мы будем использовать как строчные, так и прописные примеры. Мы покажем вам разницу между режимами и как их эффективно использовать.
2525
2626# Директивы в стиле Turbo Pascal
2727
2828Директивами в стиле Turbo Pascal являются `{$DEFINE}`, `{$IFDEF}`, `{$ENDIF}`, `{$IFNDEF}`, `{$IFOPT}`, `{$ELSE}`, `{$ELSEIF}` и `{$UNDEF}`.
2929
3030Мы опишем директивы в контексте стиля. Некоторые определения имеют расширенное значение в другом стиле.
3131
3232Это означает, что в дальнейшем мы можем расширить значение некоторых директив, таких как, например, `{$DEFINE}`, в контексте макросов.
3333
3434## \$define
3535
3636Директива `{$DEFINE}` просто объявляет символ, который мы позже можем использовать для условной компиляции:
3737
3838``` pascal
3939{$DEFINE name} //Это объявляет символ под названием "name"
4040```
4141
4242Обратите внимание, что вы также можете объявить символ из [[Command-line interface|командной строки]] или , например, -dDEBUG [[IDE]], который будет являться эквивалентом командной строки
4343
4444``` pascal
4545{$DEFINE DEBUG}
4646```
4747
4848в исходном коде.
4949
5050## \$undef
5151
5252Директива `{$UNDEF}` отменяет объявление ранее определенного символа. Вот пример, который автор использует на практике:
5353
5454``` pascal
5555// Некоторый старый исходный код, "загрязненый" обилием {$IFDEF FPC}, который больше
5656// не нуждается в зависимости от версии Delphi, с которой он должен быть совместим.
5757// Я всегда проверяю это, пробуя это поверх программы или модуля:
5858{$IFDEF FPC}
5959 {$MODE DELPHI}
6060 {$UNDEF FPC}
6161 {$DEFINE VER150}
6262 // код теперь будет компилироваться так, как если бы это был Delphi 7, при условии, что исходный код Delphi действительно был написан для Delphi 7 и выше.
6363{$ENDIF}
6464```
6565
6666## \$ifdef и \$endif
6767
6868Самый простой способ объявить блок условного кода так:
6969
7070``` pascal
7171unit cross;
7272{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
7373```
7474
7575Приведенный выше пример довольно распространен для исходного кода, который должен компилироваться как в Delphi, так и в Free Pascal.
7676
7777Если компилятором является Delphi, то ничего не делается, но если компилятором является Free Pascal, он переключит Free Pascal для компиляции и использования режима синтаксиса Delphi.
7878
7979Этот условный символ "FPC" объявлен в системе - там их длинный список. Синтаксис блоков `{$IFDEF}` и `{$ENDIF}` является симметричным: каждый `{$IFDEF}` имеет свой собственный `{$ENDIF}`.
8080
8181Чтобы помочь вам распознать соответствующие блоки, вы можете использовать, например, отступ, но вы также можете использовать функцию [[Comments|комментариев]]:
8282
8383``` pascal
8484{$IFDEF FPC эта часть специфична для Free Pascal}
8585// некий Free Pascal специфичный код
8686{$ENDIF Free Pascal специфичный код}
8787```
8888
8989> **Warning:** Эта особенность комментариев часто не совсем понятна. Некоторые люди - как и в более старой версии этой записи вики - предположили, что вы можете вкладывать `{$IFDEF}`, потому что компилятор, кажется, принимает синтаксис. Но первое неверно, а второе верно: да, компилятор принимает приведенный ниже синтаксис, но это не вложенное `{$IFDEF}`, а одиночное условие `{$IFDEF}`, а остальное - комментарий!
9090>
9191> Приведенный ниже код выполнит writeln тогда и только тогда, когда будет объявлен `{$define red}`. В этом примере `{$ifdef blue}` является комментарием! Даже если `{$define blue}` допустим.
9292
9393``` pascal
9494// программа формально будет скомпилирована, но полностью неработоспособна
9595{$define blue}
9696begin
9797{$ifdef red or $ifdef blue}// все после red - это комментарий
9898 writeln ('red or blue'); // этот код никогда не выполнится
9999{$endif red or blue} // все, что объявлено после $endif, является комментарием.
100100end.
101101```
102102
103103## \$ifndef
104104
105105Это противоположно `{$IFDEF}`, и ниже лежащий код выполнится, если объявленное условие не выполняется. Простой пример:
106106
107107``` pascal
108108{$IFNDEF FPC эта часть не для Free Pascal}
109109// некий конкретный код, который Free Pascal не должен компилировать
110110{$ENDIF код для других компиляторов, кроме Free Pascal}
111111```
112112
113113## \$else и \$elseif
114114
115115`{$ELSE}` используется для компиляции кода, который не принадлежит блоку кода, который объявлен соответствующим `{$IFDEF}`. Он также действителен в контексте `{$IFOPT}`, `{$IF}` или `{$IFC}`, которые мы обсудим позже.
116116
117117``` pascal
118118{$IFDEF red}
119119 writeln('Red объявлен');
120120{$ELSE no red}
121121 {$IFDEF blue}
122122 writeln('Blue объявлен, а red - не объявлен');
123123 {$ELSE no blue}
124124 writeln('Ни red, ни blue не объявлены');
125125 {$ENDIF blue}
126126{$ENDIF red}
127127```
128128
129129Такие вложенные условные выражения, написанные в приведенном выше синтаксисе, могут стать очень путанными и нечитаемыми. К счастью, мы можем упростить это, используя `{$ELSEIF}`. Код ниже является расширенным эквивалентом первого примера:
130130
131131``` pascal
132132{$IF Defined(red)}
133133 writeln('Red объявлен');
134134{$ELSEIF Defined(blue)}
135135 writeln('Blue объявлен');
136136{$ELSEIF Defined(green)}
137137 writeln('Green объявлен');
138138{$ELSE}
139139 writeln('Ни red, ни blue, ни green не объявлены. Должен быть black...или что-нибудь еще...');
140140{$ENDIF}
141141```
142142
143143Как вы можете видеть, это гораздо более читабельно.
144144
145145## \$ifopt
146146
147147С помощью `{$IFOPT}` мы можем проверить, задана ли определенная опция компиляции.
148148
149149Из руководства по программированию:
150150
151-` ``{$IFOPT switch}`` скомпилирует текст, который следует за ним, если переключатель switch `\
152-` в данный момент будет находиться в указанном состоянии. Если он не находится в указанном состоянии, `\
153-` то компиляция продолжится после соответствующей директивы ``{$ELSE}`` или ``{$ENDIF}``.`
151+```text
152+ {$IFOPT switch} скомпилирует текст, который следует за ним, если переключатель switch
153+ в данный момент будет находиться в указанном состоянии. Если он не находится в указанном состоянии,
154+ то компиляция продолжится после соответствующей директивы {$ELSE} или {$ENDIF}.
155+```
154156
155157Например:
156158
157159``` pascal
158160 {$IFOPT M+}
159161 Writeln('Compiled with type information');
160162 {$ENDIF}
161163```
162164
163165Скомпилирует оператор Writeln, только если включена генерация информации о типе.
164166
165167> **Note:** директива `{$IFOPT}` принимает только короткие опции, т.е. `{$IFOPT TYPEINFO}` не будет принята.
166168
167169Обычно этот пример используется для проверки, задан ли режим DEBUG:
168170
169171``` pascal
170172{$IFOPT D+}{$NOTE debug mode is active}{$ENDIF}
171173```
172174
173175Такие определения также могут находиться в конфигурационных файлах, таких как fpc.cfg, которые также содержат полное объяснение того, как использовать:
174176
175-`# ----------------------`\
176-`# Defines (preprocessor)`\
177-`# ----------------------`\
178-`#`\
179-`# nested #IFNDEF, #IFDEF, #ENDIF, #ELSE, #DEFINE, #UNDEF are allowed`\
180-`#`\
181-`# -d is the same as #DEFINE`\
182-`# -u is the same as #UNDEF`\
183-`#`\
184-`#`\
185-`# Some examples (for switches see below, and the -? help pages)`\
186-`#`\
187-`# Try compiling with the -dRELEASE or -dDEBUG on the command line`\
188-`#`\
189-`# For a release compile with optimizes and strip debug info`\
190-`#IFDEF RELEASE`\
191-`  -O2`\
192-`  -Xs`\
193-`  #WRITE Compiling Release Version`\
194-`#ENDIF`
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+```
195199
196200# Как не нужно делать
197201
198202Что не так с этим кодом? Вы можете это заметить?
199203
200204``` pascal
201205var
202206 MyFilesize:
203207 {$ifdef Win32}
204208 Cardinal
205209 {$else}
206210 int64
207211 {$endif}
208212```
209213
210214Вот ответ:
211215
212216- Free Pascal компилируется для большего количества типов процессоров, чем [[32 bit|32]] и [[64 bit]], например, для 8 и 16 бит.
213217- на большинстве 64-битных платформ максимальный размер файла - это [[QWord]], а не [[Int64]].
214218
215219Этот программист попал в ловушку, которая распространена: если вы используете объявление, убедитесь, что ваша логика надежна. В противном случае такой код может легко вызвать несчастные случаи. Компилятор не будет ловить ваши логические ошибки!
216220
217221Всегда хорошо осознавать такие вещи, тем более что такие вещи легко исправить.
218222
219223``` pascal
220224var
221225 MyFilesize:
222226 {$if defined(Win32)}
223227 Cardinal
224228 {$elseif defined(Win64)}
225229 Qword;
226230 {$else}
227231 {$error этот код написан для win32 или win64}
228232 {$endif}
229233```
230234
231235Помимо этого, есть решение для этого конкретного примера, которое вообще не использует условные выражения:
232236
233237``` pascal
234238var
235239 MyFilesize:NativeUint;
236240```
237241
238242Что не так с этим кодом? Вы можете это заметить?
239243
240244``` pascal
241245
242246{$IFDEF BLUE AND $IFDEF RED} Form1.Color := clYellow; {$ENDIF}
243247{$IFNDEF RED AND $IFNDEF BLUE} Form1.Color := clAqua; {$ENDIF}
244248```
245249
246250Вот ответ:
247251
248252- Ну, я уже написал комментарий, который предупредил вас .. так что посмотрите на предупреждение .... Вы должны быть в состоянии определить это ...
249253- Директивы компилятора переопределяют компилятор ... будьте осторожны с этим [топором Юджина](https://ru.wikipedia.org/wiki/Careful_with_That_Axe,_Eugene).
250254
251255---
252256*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).*

Back to history