Sign in Join
FPDoc Onlineexperiment

Compile time expressions

Definition

In addition to the regular Turbo Pascal constructs for conditional compilation, the Free Pascalcompiler also supports a stronger conditional compile mechanism: The {$IF} construct, which can be used to evaluate compile-time expressions.

The prototype of this construct is as follows:

{$if expr}
  CompileTheseLines;
{$else}
  BetterCompileTheseLines;
{$endif}

The content of an expression is restricted to what can be evaluated at compile-time:

  • Constants (strings, numbers)

  • Macros

  • Compile time variables (mode MacPas only)

  • Pascal constant expressions (mode Delphi only)

The symbols are replaced with their value. For macros recursive substitution might occur.

The following boolean operators are available:

=,  <>,  >,  <,  >=,  <=, AND,  NOT,  OR,  IN 

The IN operator tests for presence of a compile-time variable in a set.

In addition, the following arithmetic operators are available:

+,  -,  *,  /,  div, mod, shl, shr

The usual precedence of operators applies.

The following functions are also available:

TRUE

Defined in MacPas mode only, it evaluates to True. In other modes, 1 can be used.

FALSE

Defined in MacPas mode only, it evaluates to False. In other modes, 0 can be used.

DEFINED(sym)

will evaluate to TRUE if a compile time symbol is defined. In MacPas mode, the parentheses are optional, i.e.

{$IF DEFINED(MySym)}

is equivalent to

{$IF DEFINED MySym}

Note that you cannot use a macro that resolves to a symbol, the symbol name must be used.

UNDEFINED sym

will evaluate to TRUE if a compile time symbol is not defined, and FALSE otherwise (mode MacPas only). Note that you cannot use a macro that resolves to a symbol, the symbol name must be used.

OPTION(opt)

evaluates to TRUE if a compiler option is set (mode MacPas only). It is equivalent to the {$IFOPT } directive.

SIZEOF(passym)

Evaluates to the size of a pascal type, variable or constant. Note that you cannot use a macro that resolves to a symbol, the symbol name must be used.

DECLARED(passym)

Evaluates to TRUE if the pascal symbol is declared at this point in the sources, or FALSE if it is not yet defined. Note that you cannot use a macro that resolves to a symbol, the symbol name must be used.

In expressions, the following rules are used for evaluation:

  • If all parts of the expression can be evaluated as booleans (with 1 and 0 representing TRUE and FALSE), the expression is evaluated using booleans.

  • If all parts of the expression can be evaluated as numbers, then the expression is evaluated using numbers.

  • In all other cases, the expression is evaluated using strings.

If the complete expression evaluates to '0', then it is considered False and rejected. Otherwise it is considered True and accepted. This may have unexpected consequences:

{$if 0}

will evaluate to False and be rejected, while

{$if 00}

will evaluate to True.

Usage

The basic usage of compile time expressions is as follows:

{$if expr}
  CompileTheseLines;
{$endif}

If expr evaluates to TRUE, then CompileTheseLines will be included in the source.

Like in regular pascal, it is possible to use {$ELSE }:

{$if expr}
  CompileTheseLines;
{$else}
  BetterCompileTheseLines;
{$endif}

If expr evaluates to True, CompileTheseLines will be compiled. Otherwise, BetterCompileTheseLines will be compiled.

Additionally, it is possible to use {$ELSEIF}

{$IF  expr}
  // ...
{$ELSEIF  expr}
  // ...
{$ELSEIF  expr}
  // ...
{$ELSE}
  // ...
{$ENDIF}

In addition to the above constructs, which are also supported by Delphi, the above is completely equivalent to the following construct in MacPas mode:

{$IFC  expr}
  //...
{$ELIFC  expr}
...
{$ELIFC  expr}
...
{$ELSEC}
...
{$ENDC}

that is, IFC corresponds to IF, ELIFC corresponds to ELSEIF, ELSEC is equivalent with ELSE and ENDC is the equivalent of ENDIF. Additionally, IFEND is equivalent to ENDIF:

{$IF EXPR}
  CompileThis;
{$ENDIF}

In MacPas mode it is possible to mix these constructs.

The following example shows some of the possibilities:

{$ifdef fpc}

var
   y : longint;
{$else fpc}

var
   z : longint;
{$endif fpc}

var
   x : longint;

begin

{$IF (FPC_VERSION > 2) or 
     ((FPC_VERSION = 2) 
       and ((FPC_RELEASE > 0) or 
            ((FPC_RELEASE = 0) and (FPC_PATCH >= 1))))}
   {$DEFINE FPC_VER_201_PLUS}
 {$ENDIF}
{$ifdef FPC_VER_201_PLUS}
{$info At least this is version 2.0.1}
{$else}
{$fatal Problem with version check}
{$endif}

{$define x:=1234}
{$if x=1234}
{$info x=1234}
{$else}
{$fatal x should be 1234}
{$endif}

{$if 12asdf and 12asdf}
{$info $if 12asdf and 12asdf is ok}
{$else}
{$fatal $if 12asdf and 12asdf rejected}
{$endif}

{$if 0 or 1}
{$info $if 0 or 1 is ok}
{$else}
{$fatal $if 0 or 1 rejected}
{$endif}

{$if 0}
{$fatal $if 0 accepted}
{$else}
{$info $if 0 is ok}
{$endif}

{$if 12=12}
{$info $if 12=12 is ok}
{$else}
{$fatal $if 12=12 rejected}
{$endif}

{$if 12<>312}
{$info $if 12<>312 is ok}
{$else}
{$fatal $if 12<>312 rejected}
{$endif}


{$if 12<=312}
{$info $if 12<=312 is ok}
{$else}
{$fatal $if 12<=312 rejected}
{$endif}

{$if 12<312}
{$info $if 12<312 is ok}
{$else}
{$fatal $if 12<312 rejected}
{$endif}

{$if a12=a12}
{$info $if a12=a12 is ok}
{$else}
{$fatal $if a12=a12 rejected}
{$endif}

{$if a12<=z312}
{$info $if a12<=z312 is ok}
{$else}
{$fatal $if a12<=z312 rejected}
{$endif}


{$if a12<z312}
{$info $if a12<z312 is ok}
{$else}
{$fatal $if a12<z312 rejected}
{$endif}

{$if not(0)}
{$info $if not(0) is OK}
{$else}
{$fatal $if not(0) rejected}
{$endif}

{$IF NOT UNDEFINED FPC} 
// Detect FPC stuff when compiling on MAC.
{$SETC TARGET_RT_MAC_68881:= FALSE}
{$SETC TARGET_OS_MAC      := (NOT UNDEFINED MACOS) 
                             OR (NOT UNDEFINED DARWIN)}
{$SETC TARGET_OS_WIN32    := NOT UNDEFINED WIN32}
{$SETC TARGET_OS_UNIX     := (NOT UNDEFINED UNIX) 
                             AND (UNDEFINED DARWIN)}
{$SETC TYPE_EXTENDED      := TRUE}
{$SETC TYPE_LONGLONG      := FALSE}
{$SETC TYPE_BOOL          := FALSE}
{$ENDIF}

// Some arithmetic operators
{$IF (1 + 2) > 3}
  {$fatal 'Wrong 1'}
{$ENDIF}
{$IF (2 mod 2)= 0}
  (info 'Correct 2'}
{$ENDIF}
{$IF (2 div 2)= 1}
  {$info 'Correct 3'}
{$ENDIF}
{$IF (1 shl 2)= 4}
  {$info 'Correct 4'}
{$ENDIF}


{$info *************************************************}
{$info * Now have to follow at least 2 error messages: *}
{$info *************************************************}

{$if not(0}
{$endif}

{$if not(<}
{$endif}

end.

As you can see from the example, this construct isn’t useful when used with normal symbols, only if you use macros, which are explained in section Using conditionals, messages and macros. They can be very useful. When trying this example, you must switch on macro support, with the -Sm command line switch.

The following example works only in MacPas mode:

{$SETC TARGET_OS_MAC := (NOT UNDEFINED MACOS) OR (NOT UNDEFINED DARWIN)}

{$SETC DEBUG := TRUE}
{$SETC VERSION := 4}
{$SETC NEWMODULEUNDERDEVELOPMENT := (VERSION >= 4) OR DEBUG}

{$IFC NEWMODULEUNDERDEVELOPMENT}
  {$IFC TARGET_OS_MAC}
  ... new mac code
  {$ELSEC}
  ... new other code
  {$ENDC}
{$ELSEC}
... old code
{$ENDC}

An experiment, built from development snapshots. Not the official Free Pascal or Lazarus documentation.