Sign in Join
Wiki copytemporary

Conditional compilation/ru: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: Conditional compilation/ru

OldNewLine
1+# Что такое условная компиляция?
2+
3+Условная компиляция - это компиляция или пропуск части [[Source code|исходного кода]] в зависимости от того, существует условие или нет.
4+
5+Функции, которые делают это возможным в большинстве компилируемых языков, называются директивами времени компиляции. Директивы времени компиляции позволяют компилировать блок кода на основе наличия или отсутствия условия во [[Compile time|время компиляции]]. Они являются частью [[Compiler directive|директив компилятора]].
6+
7+Они могут использоваться для различных целей, таких как:
8+
9+- изоляция кода для конкретной платформы
10+- выбор естественного языка
11+- лицензирование частей с открытым и закрытым исходным кодом
12+- изоляция экспериментального кода
13+- версия [[Compiler|компилятора]]
14+- версия библиотеки
15+- и т.д. и т.п.
16+
17+[[FPC/ru|Free Pascal]] поддерживает четыре различных стиля условной компиляции:
18+
19+- [[Turbo Pascal]] и ранние директивы стиля [[Delphi]]
20+- директивы стиля [[Mac Pascal]]
21+- Современные директивы в стиле Free Pascal и Delphi
22+- Макросы времени компиляции
23+
24+> **Note:** здесь синтаксис не [[case-sensitive|чувствителен к регистру]], поскольку соответствует всему синтаксису [[Pascal]]. Мы будем использовать как строчные, так и прописные примеры. Мы покажем вам разницу между режимами и как их эффективно использовать.
25+
26+# Директивы в стиле Turbo Pascal
27+
28+Директивами в стиле Turbo Pascal являются `{$DEFINE}`, `{$IFDEF}`, `{$ENDIF}`, `{$IFNDEF}`, `{$IFOPT}`, `{$ELSE}`, `{$ELSEIF}` и `{$UNDEF}`.
29+
30+Мы опишем директивы в контексте стиля. Некоторые определения имеют расширенное значение в другом стиле.
31+
32+Это означает, что в дальнейшем мы можем расширить значение некоторых директив, таких как, например, `{$DEFINE}`, в контексте макросов.
33+
34+## \$define
35+
36+Директива `{$DEFINE}` просто объявляет символ, который мы позже можем использовать для условной компиляции:
37+
38+``` pascal
39+{$DEFINE name} //Это объявляет символ под названием "name"
40+```
41+
42+Обратите внимание, что вы также можете объявить символ из [[Command-line interface|командной строки]] или , например, <b>-dDEBUG</b> [[IDE]], который будет являться эквивалентом командной строки
43+
44+``` pascal
45+{$DEFINE DEBUG}
46+```
47+
48+в исходном коде.
49+
50+## \$undef
51+
52+Директива `{$UNDEF}` отменяет объявление ранее определенного символа. Вот пример, который автор использует на практике:
53+
54+``` pascal
55+// Некоторый старый исходный код, "загрязненый" обилием {$IFDEF FPC}, который больше
56+// не нуждается в зависимости от версии Delphi, с которой он должен быть совместим.
57+// Я всегда проверяю это, пробуя это поверх программы или модуля:
58+{$IFDEF FPC}
59+ {$MODE DELPHI}
60+ {$UNDEF FPC}
61+ {$DEFINE VER150}
62+ // код теперь будет компилироваться так, как если бы это был Delphi 7, при условии, что исходный код Delphi действительно был написан для Delphi 7 и выше.
63+{$ENDIF}
64+```
65+
66+## \$ifdef и \$endif
67+
68+Самый простой способ объявить блок условного кода так:
69+
70+``` pascal
71+unit cross;
72+{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
73+```
74+
75+Приведенный выше пример довольно распространен для исходного кода, который должен компилироваться как в Delphi, так и в Free Pascal.
76+
77+Если компилятором является Delphi, то ничего не делается, но если компилятором является Free Pascal, он переключит Free Pascal для компиляции и использования режима синтаксиса Delphi.
78+
79+Этот условный символ "FPC" объявлен в системе - там их длинный список. Синтаксис блоков `{$IFDEF}` и `{$ENDIF}` является симметричным: каждый `{$IFDEF}` имеет свой собственный `{$ENDIF}`.
80+
81+Чтобы помочь вам распознать соответствующие блоки, вы можете использовать, например, отступ, но вы также можете использовать функцию [[Comments|комментариев]]:
82+
83+``` pascal
84+{$IFDEF FPC эта часть специфична для Free Pascal}
85+// некий Free Pascal специфичный код
86+{$ENDIF Free Pascal специфичный код}
87+```
88+
89+> **Warning:** Эта особенность комментариев часто не совсем понятна. Некоторые люди - как и в более старой версии этой записи вики - предположили, что вы можете вкладывать `{$IFDEF}`, потому что компилятор, кажется, принимает синтаксис. Но первое неверно, а второе верно: да, компилятор принимает приведенный ниже синтаксис, но это не вложенное `{$IFDEF}`, а одиночное условие `{$IFDEF}`, а остальное - комментарий!
90+>
91+> Приведенный ниже код выполнит <i>writeln</i> <b>тогда и только тогда, когда будет объявлен</b> `{$define red}`. В этом примере `{$ifdef blue}` является комментарием! Даже если `{$define blue}` допустим.
92+
93+``` pascal
94+// программа формально будет скомпилирована, но полностью неработоспособна
95+{$define blue}
96+begin
97+{$ifdef red or $ifdef blue}// все после red - это комментарий
98+ writeln ('red or blue'); // этот код никогда не выполнится
99+{$endif red or blue} // все, что объявлено после $endif, является комментарием.
100+end.
101+```
102+
103+## \$ifndef
104+
105+Это противоположно `{$IFDEF}`, и ниже лежащий код выполнится, если объявленное условие <b>не</b> выполняется. Простой пример:
106+
107+``` pascal
108+{$IFNDEF FPC эта часть не для Free Pascal}
109+// некий конкретный код, который Free Pascal не должен компилировать
110+{$ENDIF код для других компиляторов, кроме Free Pascal}
111+```
112+
113+## \$else и \$elseif
114+
115+`{$ELSE}` используется для компиляции кода, который не принадлежит блоку кода, который объявлен соответствующим `{$IFDEF}`. Он также действителен в контексте `{$IFOPT}`, `{$IF}` или `{$IFC}`, которые мы обсудим позже.
116+
117+``` pascal
118+{$IFDEF red}
119+ writeln('Red объявлен');
120+{$ELSE no red}
121+ {$IFDEF blue}
122+ writeln('Blue объявлен, а red - не объявлен');
123+ {$ELSE no blue}
124+ writeln('Ни red, ни blue не объявлены');
125+ {$ENDIF blue}
126+{$ENDIF red}
127+```
128+
129+Такие вложенные условные выражения, написанные в приведенном выше синтаксисе, могут стать очень путанными и нечитаемыми. К счастью, мы можем упростить это, используя `{$ELSEIF}`. Код ниже является расширенным эквивалентом первого примера:
130+
131+``` pascal
132+{$IF Defined(red)}
133+ writeln('Red объявлен');
134+{$ELSEIF Defined(blue)}
135+ writeln('Blue объявлен');
136+{$ELSEIF Defined(green)}
137+ writeln('Green объявлен');
138+{$ELSE}
139+ writeln('Ни red, ни blue, ни green не объявлены. Должен быть black...или что-нибудь еще...');
140+{$ENDIF}
141+```
142+
143+Как вы можете видеть, это гораздо более читабельно.
144+
145+## \$ifopt
146+
147+С помощью `{$IFOPT}` мы можем проверить, задана ли определенная опция компиляции.
148+
149+Из руководства по программированию:
150+
151+` ``{$IFOPT switch}`` скомпилирует текст, который следует за ним, если переключатель switch `\
152+` в данный момент будет находиться в указанном состоянии. Если он не находится в указанном состоянии, `\
153+` то компиляция продолжится после соответствующей директивы ``{$ELSE}`` или ``{$ENDIF}``.`
154+
155+Например:
156+
157+``` pascal
158+ {$IFOPT M+}
159+ Writeln('Compiled with type information');
160+ {$ENDIF}
161+```
162+
163+Скомпилирует оператор Writeln, только если включена генерация информации о типе.
164+
165+> **Note:** директива `{$IFOPT}` принимает только короткие опции, т.е. `{$IFOPT TYPEINFO}` не будет принята.
166+
167+Обычно этот пример используется для проверки, задан ли режим DEBUG:
168+
169+``` pascal
170+{$IFOPT D+}{$NOTE debug mode is active}{$ENDIF}
171+```
172+
173+Такие определения также могут находиться в конфигурационных файлах, таких как fpc.cfg, которые также содержат полное объяснение того, как использовать:
174+
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`
195+
196+# Как не нужно делать
197+
198+Что не так с этим кодом? Вы можете это заметить?
199+
200+``` pascal
201+var
202+ MyFilesize:
203+ {$ifdef Win32}
204+ Cardinal
205+ {$else}
206+ int64
207+ {$endif}
208+```
209+
210+Вот ответ:
211+
212+- Free Pascal компилируется для большего количества типов процессоров, чем [[32 bit|32]] и [[64 bit]], например, для 8 и 16 бит.
213+- на большинстве 64-битных платформ максимальный размер файла - это [[QWord]], а не [[Int64]].
214+
215+Этот программист попал в ловушку, которая распространена: если вы используете объявление, убедитесь, что ваша логика надежна. В противном случае такой код может легко вызвать несчастные случаи. Компилятор не будет ловить ваши логические ошибки!
216+
217+Всегда хорошо осознавать такие вещи, тем более что такие вещи легко исправить.
218+
219+``` pascal
220+var
221+ MyFilesize:
222+ {$if defined(Win32)}
223+ Cardinal
224+ {$elseif defined(Win64)}
225+ Qword;
226+ {$else}
227+ {$error этот код написан для win32 или win64}
228+ {$endif}
229+```
230+
231+Помимо этого, есть решение для этого конкретного примера, которое вообще не использует условные выражения:
232+
233+``` pascal
234+var
235+ MyFilesize:NativeUint;
236+```
237+
238+Что не так с этим кодом? Вы можете это заметить?
239+
240+``` pascal
241+
242+{$IFDEF BLUE AND $IFDEF RED} Form1.Color := clYellow; {$ENDIF}
243+{$IFNDEF RED AND $IFNDEF BLUE} Form1.Color := clAqua; {$ENDIF}
244+```
245+
246+Вот ответ:
247+
248+- Ну, я уже написал <b>комментарий</b>, который предупредил вас .. так что посмотрите на предупреждение .... Вы должны быть в состоянии определить это ...
249+- Директивы компилятора переопределяют компилятор ... будьте осторожны с этим [топором Юджина](https://ru.wikipedia.org/wiki/Careful_with_That_Axe,_Eugene).
250+
251+---
252+*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