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

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

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

SomeConstantValue = 5;

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

It was added to the Delphi compiler in Delphi 6, so it covers quite a few Delphi versions.
Combined with predefined constants (in the System unit) like

FireMonkeyVersion (in FMX.Types)

this is a powerful method for conditional compilation.

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

It can also replace those additional symbols DELPHInn_UP I mentioned above: “>=” replaces {$IFDEF DELPHInn_UP} and “=” 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

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;


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:
RtlVersionDelphi6 = 14;
RtlVersionDelphi7 = 15;
// and so on until
RtlVersionDelphiRio = 33;


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

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

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: 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.

Comments are closed.