Sign in Join
Wiki copytemporary

Conditional compilation: what changed

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

Title: Conditional compilation

OldNewLine
1+**Conditional compilation** refers to compiling or omitting parts of [[Source code|source code]] based on an [[compile time expressions|expression evaluated at compile-time]]. This allows taking account of, for example, different interfaces or architectures of specific [[operating system|operating systems]] or platforms, while still being able to program in a generic way.
2+
3+## Support
4+
5+Conditional compilation needs to be supported in some way or other. Some [[Compiler|compilers]] need an additional tool called *pre-processor*, [[FPC]] however has all required functionality built-in.
6+
7+For FPC and in de-facto most compiled languages, conditional compilation is implemented by specially crafted comments that are then seen as [[Compiler directive|compiler directives]]. These surround any arbitrary amount of code that may be ignored or remain included based on an expression provided as evaluated at [[Compile time|compile-time]].
8+
9+They can be used for a variety of purposes like:
10+
11+- Platform specific code isolation
12+- Natural language selection (where `resourceString`s do not suffice)
13+- Licensing opensource and closed source parts
14+- Isolating experimental code
15+- Compiler version: certain compiler features may have been present only since a certain version
16+- Library version: interfaces may have changed with certain versions
17+- etc., etc.
18+
19+## Relevant compiler directives
20+
21+FPC supports four different styles of conditional compilation:
22+
23+- [[Turbo Pascal]] and early [[Delphi]] style directives
24+- [[Mac Pascal]] style directives
25+- Modern Free Pascal and Delphi style directives
26+- Compile time Macros
27+
28+Note the syntax here is not [[case-sensitive|case sensitive]] as conforms to all [[Pascal]] syntax. We will use both lowercase and uppercase examples. We will show you the difference between the modes and how to efficiently use them.
29+
30+### Turbo Pascal style directives
31+
32+The Turbo Pascal style directives are
33+
34+- `{$DEFINE}`,
35+- `{$IFDEF}`,
36+- `{$ENDIF}`,
37+- `{$IFNDEF}`,
38+- `{$IFOPT}`,
39+- `{$ELSE}`,
40+- `{$ELSEIF}` and
41+- `{$UNDEF}`.
42+
43+We will describe the directives in the context of the style. Some defines have an extended meaning in another style.
44+
45+That means later on we may expand the meaning of certain directives like e. g. `{$DEFINE}`in the context of Macros.
46+
47+#### `$define`
48+
49+The `{$DEFINE}` directive simply declares a symbol that we later can use for conditional compilation:
50+
51+``` pascal
52+{$DEFINE name} // This defines a symbol called "name"
53+```
54+
55+Note you can also define a symbol from the [[Command-line interface|command line]] or the [[IDE]], for example
56+
57+``` bash
58+-dDEBUG
59+```
60+
61+is the command line equivalent of
62+
63+``` pascal
64+{$DEFINE DEBUG}
65+```
66+
67+in the source code.
68+
69+#### `$undef`
70+
71+The `{$UNDEF}` directive undefines a (presumably) previously defined symbol. Here is an example that the author uses in practice:
72+
73+``` pascal
74+// Some older source code is polluted with {$IFDEF FPC}
75+// that are no longer necessary
76+// depending on the Delphi version to which it it should be compatible.
77+// I always test this by trying this on top of the program or unit:
78+{$IFDEF FPC}
79+ {$MODE DELPHI}
80+ {$UNDEF FPC}
81+ {$DEFINE VER150}
82+ // code will now compile as if it was Delphi 7,
83+ // provided the original Delphi source code
84+ // was indeed written for Delphi 7 and up.
85+{$ENDIF}
86+```
87+
88+#### `$ifdef` and `$endif`
89+
90+The simplest way to define a block of conditional code is like this:
91+
92+``` pascal
93+unit cross;
94+{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
95+```
96+
97+The above example is quite common for source code that has to compile with both Delphi and FPC.
98+
99+If the compiler is Delphi, then nothing is done, but if the compiler is the FPC, it will configure FPC to compile and use Delphi syntax mode.
100+
101+This `FPC` conditional symbol is defined by the compiler (cf. [`compiler/options.pas`](https://svn.freepascal.org/cgi-bin/viewvc.cgi/tags/release_3_2_0/compiler/options.pas?view=markup#l3695)). The `{$IFDEF}` and `{$ENDIF}` frame syntax is symmetrical: Every `{$IFDEF}` has a matching `{$ENDIF}`.
102+
103+To help you recognize the corresponding blocks you can use e. g. indentation, but you can also use the [[Comments|comment]] feature:
104+
105+``` pascal
106+{$IFDEF FPC this part is Free Pascal specific}
107+// some Free Pascal specific code
108+{$ENDIF Free Pascal specific code}
109+```
110+
111+> **Warning:** This comment feature is often not well understood. Some people – as on an older version of this wiki entry – assumed you could nest `{$IFDEF}` because the compiler seems to accept the syntax. But the former is false and the latter is true: Yes, the compiler accepts the syntax below, but it is not a nested `{$IFDEF}` but a single {`$IFDEF}` condition and the rest is a comment!
112+>
113+> The code below executes the [[Write|writeLn]] if and only if `red` is defined. In this example `{$ifdef blue}` is a comment! Even if the `{$define blue}` is valid.
114+>
115+> ``` pascal
116+> // program completely wrong;
117+> {$define blue}
118+> begin
119+> {$ifdef red or $ifdef blue} // everything after red is a comment
120+> writeLn ('red or blue'); // this code is never reached
121+> {$endif red or blue} // everything after $endif is a comment.
122+> end.
123+> ```
124+
125+> **Note:** The comment feature is non-standard and the [[GNU Pascal|GPC]] for instance will emit a warning “`` garbage at end of `$ifdef' argument ``”.
126+
127+#### `$ifndef`
128+
129+This is the opposite of `{$IFDEF}` and code will be included of a certain condition is *not* defined. A simple example is:
130+
131+``` pascal
132+{$IFNDEF FPC this part not for Free Pascal}
133+// some specific code that Free Pascal should not compile
134+{$ENDIF code for other compilers than Free Pascal}
135+```
136+
137+#### `$else` and `$elseif`
138+
139+`{$ELSE}` is used to compile code that does not belong to the code block that is defined by the corresponding `{$IFDEF}`. It is also valid in the context `{$IFOPT}`, `{$IF}` or `{$IFC}` that we will discuss later.
140+
141+``` pascal
142+{$IFDEF red}
143+ writeLn('Red is defined');
144+{$ELSE no red}
145+ {$IFDEF blue}
146+ writeLn('Blue is defined, but red is not defined');
147+ {$ELSE no blue}
148+ writeLn('Neither red nor blue is defined');
149+ {$ENDIF blue}
150+{$ENDIF red}
151+```
152+
153+Such nested conditional written in the above syntax can get very confusing and thus is prone to errors. Luckily we can simplify it a lot by using `{$ELSEIF}`. The code below is an expanded equivalent of the first example:
154+
155+``` pascal
156+{$IF defined(red)}
157+ writeLn('Red is defined');
158+{$ELSEIF defined(blue)}
159+ writeLn('Blue is defined');
160+{$ELSEIF defined(green)}
161+ writeLn('Green is defined');
162+{$ELSE}
163+ writeLn('Neither red, blue or green. Must be black...or something else...');
164+{$ENDIF}
165+```
166+
167+As you can see this is a lot more readable.
168+
169+#### `$ifopt`
170+
171+With `{$IFOPT}` we can check if a certain compile option is defined.
172+
173+From the programmers’ manual:
174+
175+> The `{$IFOPT switch}` will compile the text that follows it if the switch switch is currently in the specified state. If it isn’t in the specified state, then compilation continues after the corresponding `{$ELSE}` or `{$ENDIF}` directive.
176+
177+As an example:
178+
179+``` pascal
180+{$IFOPT M+}
181+ writeLn('Compiled with type information');
182+{$ENDIF}
183+```
184+
185+Will compile the `writeLn` statement only if generation of type information is enabled.
186+
187+> **Note:** The `{$IFOPT}` directive accepts only short options, i. e. `{$IFOPT TYPEINFO}` will not be accepted.
188+
189+A common use is this example to test if `DEBUG` mode is defined:
190+
191+``` pascal
192+{$IFOPT D+}{$NOTE debug mode is active}{$ENDIF}
193+```
194+
195+Such defines can also reside in configuration files like `fpc.cfg` which also contains a full explanation on how to use:
196+
197+```text
198+# ----------------------
199+# Defines (preprocessor)
200+# ----------------------
201+#
202+# nested #IFNDEF, #IFDEF, #ENDIF, #ELSE, #DEFINE, #UNDEF are allowed
203+#
204+# -d is the same as #DEFINE
205+# -u is the same as #UNDEF
206+#
207+#
208+# Some examples (for switches see below, and the -? help pages)
209+#
210+# Try compiling with the -dRELEASE or -dDEBUG on the command line
211+#
212+# For a release compile with optimizes and strip debug info
213+#IFDEF RELEASE
214+ -O2
215+ -Xs
216+ #WRITE Compiling Release Version
217+#ENDIF
218+```
219+
220+## What not to do
221+
222+This is a short tutorial:
223+
224+### Cases
225+
226+What is wrong with this code? Can you spot it?
227+
228+``` pascal
229+var
230+ MyFilesize:
231+ {$ifdef Win32}
232+ Cardinal
233+ {$else}
234+ int64
235+ {$endif}
236+ ;
237+```
238+
239+<table>
240+<caption>answer</caption>
241+<tbody>
242+<tr>
243+<td><p>The answer is:</p>
244+<ul>
245+<li>that Free Pascal compiles for more CPU types than [[32 bit|32]] an [[64 bit]], also for e. g. 8 and 16 bit.</li>
246+<li>on most 64-bit platforms the maximum file size is a [[QWord]], not an [[Int64]].</li>
247+</ul>
248+<p>That programmer fell into a trap that is common: If you use a define, make sure your logic is solid. Otherwise such code can easily cause accidents. The compiler will not catch your logic errors!</p>
249+<p>It is always good to realize such things especially that such things can easily be fixed.</p>
250+<div class="sourceCode" id="cb1"><pre class="sourceCode pascal"><code class="sourceCode pascal"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span></span>
251+<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> MyFilesize:</span>
252+<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="ot">{$if defined(Win32)}</span> </span>
253+<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="dt">Cardinal</span> </span>
254+<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="ot">{$elseif defined(Win64)}</span></span>
255+<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="dt">Qword</span>;</span>
256+<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="ot">{$else}</span></span>
257+<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a> <span class="ot">{$error this code is written for win32 or win64}</span></span>
258+<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a> <span class="ot">{$endif}</span></span></code></pre></div>
259+<p>As an aside of course there is a solution for this particular example that does not use conditionals at all:</p>
260+<div class="sourceCode" id="cb2"><pre class="sourceCode pascal"><code class="sourceCode pascal"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">var</span></span>
261+<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> MyFilesize: NativeUint;</span></code></pre></div></td>
262+</tr>
263+</tbody>
264+</table>
265+
266+### Understanding
267+
268+What is wrong with this code? Can you spot it?
269+
270+``` pascal
271+{$IFDEF BLUE AND $IFDEF RED} Form1.Color := clYellow; {$ENDIF}
272+{$IFNDEF RED AND $IFNDEF BLUE} Form1.Color := clAqua; {$ENDIF}
273+```
274+
275+<table>
276+<caption>answer</caption>
277+<tbody>
278+<tr>
279+<td><p>The Answer is:</p>
280+<ul>
281+<li>Well, I have already wrote a <em>comment</em> that warned you.. so look at the warning.... You should be able to spot it...</li>
282+<li>Compiler directives override the compiler... be careful with that ax Eugene.</li>
283+</ul></td>
284+</tr>
285+</tbody>
286+</table>
287+
288+---
289+*From the Free Pascal wiki, page "Conditional compilation", as it stood on 2020-07-14 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