Conditional compilation for various Delphi versions

If you are maintaining a library, component or plugin for various Delphi versions you will sooner or later hit a point where it becomes necessary to have different code for some of them.

Some examples are:

  • The constants faTemporary and faSymlink are only declared in Delphi 2009 and later, so you have to declare them yourself for older versions.
  • Some Open Tools API function have bugs in some versions so you have to implement a workaround.
  • Some classes or functions have been added to later versions of the RTL so you need to implement them yourself for older versions, but you don’t want to use these implementations for newer versions

The traditional way of masking code for some Delphi versions is using the VERxxx symbols which the compiler defines, where xxx is the compiler version multiplied by 10.

Note that the compiler versions started with Turbo Pascal, not with Delphi, so VER60 is not Delphi 6 but Turbo Pascal 6, while Delphi 6 is compiler version 14 and therefore defines VER140. By the time of this writing the current Delphi version is 10.3 Rio which contains the compiler version 33.

{$IFDEF VER330} // Delphi 10.3 Rio
// do some version specific stuff here
{$ENDIF}

There are various include files that make this more convenient by adding symbols like DELPHInn and DELPHInn_UP so you don’t have to memorize those VERxxx symbols.

{$IFDEF DelphiX103}
// do some version specific stuff here
{$ENDIF}

But using these include files has got a major drawback: If you forget to include it into your source code, all your IFDEFS will fail and in the worst case your workaround won’t be active (the best case is that the compiler runs into an error so you will notice the missing include).

An alternative is the {$IF } compiler directive which can test for arbitrary Boolean expressions, like

const
  SomeConstantValue = 5;

//later on
{$IF SomeConstantValue >= 5}
// do some stuff here that requires SomeConstValue to be at least 5
{$IFEND}

It was added to the Delphi compiler in Delphi 6, so it covers quite a few Delphi versions. (Edit: Note that Delphi XE2 and older requires {$IF } to be closed by {$IFEND}. Newer versions also accept {$ENDIF}.)

Combined with predefined constants (in the System unit) like

this is a powerful method for conditional compilation.

{$IF CompilerVersion = 330} // Delphi 10.3 Rio
// do some version specific stuff here
{$IFEND}

It can also replace those additional symbols DELPHInn_UP I mentioned above: “>=” replaces {$IFDEF DELPHInn_UP} and “<=" replaces {$IFNDEF DELPHInn_UP}. Also by using ">=” you can “future proof” your code, assuming that code for Delphi 10.3 Rio will also work with all newer versions of Delphi.

{$IF CompilerVersion >= 330} // Delphi 10.3 Rio
// do some version specific stuff here that will
// hopefully also work in the future
{$IFEND}

But unfortunately we are back to memorizing compiler and RTL version constants. Even with tools like the IF Directive Expert in GExperts this is a nuisance because if you forget to add a comment (or if you later change the expression and forget to update the comment), you will still have to know those values to understand the code.

So, what can be done? An idea that occurred to me today (Yes, I am a bit slow on creativity.) would be to define additional constants which can then be used to compare against the CompilerVersion and RtlVersion constants.

unit CompilerAndRtlVersions;

interface

const
  CompilerVersionDelphi6 = 14;
  CompilerVersionDelphi7 = 15;
  // we all want to forget Delphi 8, but it had compiler version 16
  CompilerVersionDelphi2005 = 17;
  CompilerVersionDelphi2006 = 18;
  CompilerVersionDelphi2007 = 18.5;
  // anybody remember Delphi 2007 for dotNET? That one had compiler version 19
  CompilerVersionDelphi2009 = 20;
  // and so on until
  CompilerVersionDelphiRio = 33;

// and of course we would also need the RtlVersions:
const
  RtlVersionDelphi6 = 14;
  RtlVersionDelphi7 = 15;
  // and so on until
  RtlVersionDelphiRio = 33;

implementation
end.

Add the above unit to the uses clause and you can do:


{$IF CompilerVersion >= CompilerVersionDelphiRio}
// do some version specific stuff here
{$IFEND}

These constants should go into a unit rather into an include file so the compiler will complain if you forget to add that unit to the uses clause. In addition a unit will be compiled once and afterwards the compiler will use the DCU file it created, while an include fill will be parsed every single time it is included. This should speed up compilation a (probably tiny) bit.

I am not aware if such a unit already exists, but I would probably write it and make it available if not (it’s not excactly rocket science after all).
EDIT: I’m not the first one who came up with this idea.

Edit: Here you go, my brand new u_dzCompilerAndRtlVersions unit.

I already blogged about using and ab-using ifdef in 2013, if you are interested in this topic.