Conditional compilation
│
Deutsch (de) │
English (en) │
suomi (fi) │
français (fr) │
русский (ru) │
Conditional compilation refers to compiling or omitting parts of source code based on an expression evaluated at compile-time. This allows taking account of, for example, different interfaces or architectures of specific operating systems or platforms, while still being able to program in a generic way.
Support
Conditional compilation needs to be supported in some way or other. Some compilers need an additional tool called pre-processor, FPC however has all required functionality built-in.
For FPC and in de-facto most compiled languages, conditional compilation is implemented by specially crafted comments that are then seen as 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.
They can be used for a variety of purposes like:
- Platform specific code isolation
- Natural language selection (where
resourceString
s do not suffice) - Licensing opensource and closed source parts
- Isolating experimental code
- Compiler version: certain compiler features may have been present only since a certain version
- Library version: interfaces may have changed with certain versions
- etc., etc.
Relevant compiler directives
FPC supports four different styles of conditional compilation:
- Turbo Pascal and early Delphi style directives
- Mac Pascal style directives
- Modern Free Pascal and Delphi style directives
- Compile time Macros
Note the syntax here is not 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.
Turbo Pascal style directives
The Turbo Pascal style directives are
{$DEFINE}
,{$IFDEF}
,{$ENDIF}
,{$IFNDEF}
,{$IFOPT}
,{$ELSE}
,{$ELSEIF}
and{$UNDEF}
.
We will describe the directives in the context of the style. Some defines have an extended meaning in another style.
That means later on we may expand the meaning of certain directives like e. g. {$DEFINE}
in the context of Macros.
$define
The {$DEFINE}
directive simply declares a symbol that we later can use for conditional compilation:
{$DEFINE name} // This defines a symbol called "name"
Note you can also define a symbol from the command line or the IDE, for example
-dDEBUG
is the command line equivalent of
{$DEFINE DEBUG}
in the source code.
$undef
The {$UNDEF}
directive undefines a (presumably) previously defined symbol.
Here is an example that the author uses in practice:
// Some older source code is polluted with {$IFDEF FPC}
// that are no longer necessary
// depending on the Delphi version to which it it should be compatible.
// I always test this by trying this on top of the program or unit:
{$IFDEF FPC}
{$MODE DELPHI}
{$UNDEF FPC}
{$DEFINE VER150}
// code will now compile as if it was Delphi 7,
// provided the original Delphi source code
// was indeed written for Delphi 7 and up.
{$ENDIF}
$ifdef
and $endif
The simplest way to define a block of conditional code is like this:
unit cross;
{$IFDEF FPC}{$MODE DELPHI}{$ENDIF}
The above example is quite common for source code that has to compile with both Delphi and FPC.
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.
This FPC
conditional symbol is defined by the compiler (cf. compiler/options.pas).
The {$IFDEF}
and {$ENDIF}
frame syntax is symmetrical:
Every {$IFDEF}
has a matching {$ENDIF}
.
To help you recognize the corresponding blocks you can use e. g. indentation, but you can also use the comment feature:
{$IFDEF FPC this part is Free Pascal specific}
// some Free Pascal specific code
{$ENDIF Free Pascal specific code}
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!
The code below executes the writeLn
if and only if red
is defined.
In this example {$ifdef blue}
is a comment!
Even if the {$define blue}
is valid.
// program completely wrong;
{$define blue}
begin
{$ifdef red or $ifdef blue} // everything after red is a comment
writeLn ('red or blue'); // this code is never reached
{$endif red or blue} // everything after $endif is a comment.
end.
garbage at end of `$ifdef' argument
”.$ifndef
This is the opposite of {$IFDEF}
and code will be included of a certain condition is not defined.
A simple example is:
{$IFNDEF FPC this part not for Free Pascal}
// some specific code that Free Pascal should not compile
{$ENDIF code for other compilers than Free Pascal}
$else
and $elseif
{$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.
{$IFDEF red}
writeLn('Red is defined');
{$ELSE no red}
{$IFDEF blue}
writeLn('Blue is defined, but red is not defined');
{$ELSE no blue}
writeLn('Neither red nor blue is defined');
{$ENDIF blue}
{$ENDIF red}
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:
{$IF defined(red)}
writeLn('Red is defined');
{$ELSEIF defined(blue)}
writeLn('Blue is defined');
{$ELSEIF defined(green)}
writeLn('Green is defined');
{$ELSE}
writeLn('Neither red, blue or green. Must be black...or something else...');
{$ENDIF}
As you can see this is a lot more readable.
$ifopt
With {$IFOPT}
we can check if a certain compile option is defined.
From the programmers’ manual:
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.
As an example:
{$IFOPT M+}
writeLn('Compiled with type information');
{$ENDIF}
Will compile the writeLn
statement only if generation of type information is enabled.
{$IFOPT}
directive accepts only short options, i. e. {$IFOPT TYPEINFO}
will not be accepted.A common use is this example to test if DEBUG
mode is defined:
{$IFOPT D+}{$NOTE debug mode is active}{$ENDIF}
Such defines can also reside in configuration files like fpc.cfg
which also contains a full explanation on how to use:
# ----------------------
# Defines (preprocessor)
# ----------------------
#
# nested #IFNDEF, #IFDEF, #ENDIF, #ELSE, #DEFINE, #UNDEF are allowed
#
# -d is the same as #DEFINE
# -u is the same as #UNDEF
#
#
# Some examples (for switches see below, and the -? help pages)
#
# Try compiling with the -dRELEASE or -dDEBUG on the command line
#
# For a release compile with optimizes and strip debug info
#IFDEF RELEASE
-O2
-Xs
#WRITE Compiling Release Version
#ENDIF
What not to do
This is a short tutorial:
Cases
What is wrong with this code? Can you spot it?
var
MyFilesize:
{$ifdef Win32}
Cardinal
{$else}
int64
{$endif}
;
The answer is:
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! It is always good to realize such things especially that such things can easily be fixed. var
MyFilesize:
{$if defined(Win32)}
Cardinal
{$elseif defined(Win64)}
Qword;
{$else}
{$error this code is written for win32 or win64}
{$endif}
As an aside of course there is a solution for this particular example that does not use conditionals at all: var
MyFilesize: NativeUint;
|
Understanding
What is wrong with this code? Can you spot it?
{$IFDEF BLUE AND $IFDEF RED} Form1.Color := clYellow; {$ENDIF}
{$IFNDEF RED AND $IFNDEF BLUE} Form1.Color := clAqua; {$ENDIF}
The Answer is:
|
Directives, definitions and conditionals definitions |
---|
global compiler directives • local compiler directives Conditional Compiler Options • Conditional compilation • Macros and Conditionals • Platform defines |