Sign in Join
Wiki copytemporary

Conditional compilation: what changed

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

OldNewLine
11**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.
22
33## Support
44
55Conditional 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.
66
77For 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]].
88
99They can be used for a variety of purposes like:
1010
1111- Platform specific code isolation
1212- Natural language selection (where `resourceString`s do not suffice)
1313- Licensing opensource and closed source parts
1414- Isolating experimental code
1515- Compiler version: certain compiler features may have been present only since a certain version
1616- Library version: interfaces may have changed with certain versions
1717- etc., etc.
1818
1919## Relevant compiler directives
2020
2121FPC supports four different styles of conditional compilation:
2222
2323- [[Turbo Pascal]] and early [[Delphi]] style directives
2424- [[Mac Pascal]] style directives
2525- Modern Free Pascal and Delphi style directives
2626- Compile time Macros
2727
2828Note 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.
2929
3030### Turbo Pascal style directives
3131
3232The Turbo Pascal style directives are
3333
3434- `{$DEFINE}`,
3535- `{$IFDEF}`,
3636- `{$ENDIF}`,
3737- `{$IFNDEF}`,
3838- `{$IFOPT}`,
3939- `{$ELSE}`,
4040- `{$ELSEIF}` and
4141- `{$UNDEF}`.
4242
4343We will describe the directives in the context of the style. Some defines have an extended meaning in another style.
4444
4545That means later on we may expand the meaning of certain directives like e. g. `{$DEFINE}`in the context of Macros.
4646
4747#### `$define`
4848
4949The `{$DEFINE}` directive simply declares a symbol that we later can use for conditional compilation:
5050
5151``` pascal
5252{$DEFINE name} // This defines a symbol called "name"
5353```
5454
5555Note you can also define a symbol from the [[Command-line interface|command line]] or the [[IDE]], for example
5656
5757``` bash
5858-dDEBUG
5959```
6060
6161is the command line equivalent of
6262
6363``` pascal
6464{$DEFINE DEBUG}
6565```
6666
6767in the source code.
6868
6969#### `$undef`
7070
7171The `{$UNDEF}` directive undefines a (presumably) previously defined symbol. Here is an example that the author uses in practice:
7272
7373``` pascal
7474// Some older source code is polluted with {$IFDEF FPC}
7575// that are no longer necessary
7676// depending on the Delphi version to which it it should be compatible.
7777// I always test this by trying this on top of the program or unit:
7878{$IFDEF FPC}
7979 {$MODE DELPHI}
8080 {$UNDEF FPC}
8181 {$DEFINE VER150}
8282 // code will now compile as if it was Delphi 7,
8383 // provided the original Delphi source code
8484 // was indeed written for Delphi 7 and up.
8585{$ENDIF}
8686```
8787
8888#### `$ifdef` and `$endif`
8989
9090The simplest way to define a block of conditional code is like this:
9191
9292``` pascal
9393unit cross;
9494{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
9595```
9696
9797The above example is quite common for source code that has to compile with both Delphi and FPC.
9898
9999If 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.
100100
101101This `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}`.
102102
103103To help you recognize the corresponding blocks you can use e. g. indentation, but you can also use the [[Comments|comment]] feature:
104104
105105``` pascal
106106{$IFDEF FPC this part is Free Pascal specific}
107107// some Free Pascal specific code
108108{$ENDIF Free Pascal specific code}
109109```
110110
111111> **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!
112112>
113113> 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.
114114>
115115> ``` pascal
116116> // program completely wrong;
117117> {$define blue}
118118> begin
119119> {$ifdef red or $ifdef blue} // everything after red is a comment
120120> writeLn ('red or blue'); // this code is never reached
121121> {$endif red or blue} // everything after $endif is a comment.
122122> end.
123123> ```
124124
125125> **Note:** The comment feature is non-standard and the [[GNU Pascal|GPC]] for instance will emit a warning “`` garbage at end of `$ifdef' argument ``”.
126126
127127#### `$ifndef`
128128
129129This is the opposite of `{$IFDEF}` and code will be included of a certain condition is *not* defined. A simple example is:
130130
131131``` pascal
132132{$IFNDEF FPC this part not for Free Pascal}
133133// some specific code that Free Pascal should not compile
134134{$ENDIF code for other compilers than Free Pascal}
135135```
136136
137137#### `$else` and `$elseif`
138138
139139`{$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.
140140
141141``` pascal
142142{$IFDEF red}
143143 writeLn('Red is defined');
144144{$ELSE no red}
145145 {$IFDEF blue}
146146 writeLn('Blue is defined, but red is not defined');
147147 {$ELSE no blue}
148148 writeLn('Neither red nor blue is defined');
149149 {$ENDIF blue}
150150{$ENDIF red}
151151```
152152
153153Such 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:
154154
155155``` pascal
156156{$IF defined(red)}
157157 writeLn('Red is defined');
158158{$ELSEIF defined(blue)}
159159 writeLn('Blue is defined');
160160{$ELSEIF defined(green)}
161161 writeLn('Green is defined');
162162{$ELSE}
163163 writeLn('Neither red, blue or green. Must be black...or something else...');
164164{$ENDIF}
165165```
166166
167167As you can see this is a lot more readable.
168168
169169#### `$ifopt`
170170
171171With `{$IFOPT}` we can check if a certain compile option is defined.
172172
173173From the programmers’ manual:
174174
175175> 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.
176176
177177As an example:
178178
179179``` pascal
180180{$IFOPT M+}
181181 writeLn('Compiled with type information');
182182{$ENDIF}
183183```
184184
185185Will compile the `writeLn` statement only if generation of type information is enabled.
186186
187187> **Note:** The `{$IFOPT}` directive accepts only short options, i. e. `{$IFOPT TYPEINFO}` will not be accepted.
188188
189189A common use is this example to test if `DEBUG` mode is defined:
190190
191191``` pascal
192192{$IFOPT D+}{$NOTE debug mode is active}{$ENDIF}
193193```
194194
195195Such defines can also reside in configuration files like `fpc.cfg` which also contains a full explanation on how to use:
196196
197197```text
198198# ----------------------
199199# Defines (preprocessor)
200200# ----------------------
201201#
202202# nested #IFNDEF, #IFDEF, #ENDIF, #ELSE, #DEFINE, #UNDEF are allowed
203203#
204204# -d is the same as #DEFINE
205205# -u is the same as #UNDEF
206206#
207207#
208208# Some examples (for switches see below, and the -? help pages)
209209#
210210# Try compiling with the -dRELEASE or -dDEBUG on the command line
211211#
212212# For a release compile with optimizes and strip debug info
213213#IFDEF RELEASE
214214 -O2
215215 -Xs
216216 #WRITE Compiling Release Version
217217#ENDIF
218218```
219219
220220## What not to do
221221
222222This is a short tutorial:
223223
224224### Cases
225225
226226What is wrong with this code? Can you spot it?
227227
228228``` pascal
229229var
230230 MyFilesize:
231231 {$ifdef Win32}
232232 Cardinal
233233 {$else}
234234 int64
235235 {$endif}
236236 ;
237237```
238238
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>
239+answer
265240
241+The answer is:
242+
243+- that Free Pascal compiles for more CPU types than [[32 bit|32]] an [[64 bit]], also for e. g. 8 and 16 bit.
244+- on most 64-bit platforms the maximum file size is a [[QWord]], not an [[Int64]].
245+
246+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!
247+
248+It is always good to realize such things especially that such things can easily be fixed.
249+
250+``` pascal
251+var
252+ MyFilesize:
253+ {$if defined(Win32)}
254+ Cardinal
255+ {$elseif defined(Win64)}
256+ Qword;
257+ {$else}
258+ {$error this code is written for win32 or win64}
259+ {$endif}
260+```
261+
262+As an aside of course there is a solution for this particular example that does not use conditionals at all:
263+
264+``` pascal
265+var
266+ MyFilesize: NativeUint;
267+```
268+
266269### Understanding
267270
268271What is wrong with this code? Can you spot it?
269272
270273``` pascal
271274{$IFDEF BLUE AND $IFDEF RED} Form1.Color := clYellow; {$ENDIF}
272275{$IFNDEF RED AND $IFNDEF BLUE} Form1.Color := clAqua; {$ENDIF}
273276```
274277
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>
278+answer
279+
280+The Answer is:
281+
282+- Well, I have already wrote a *comment* that warned you.. so look at the warning.... You should be able to spot it...
283+- Compiler directives override the compiler... be careful with that ax Eugene.
287284
288285---
289286*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