The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 4,262 other subscribers

Delphi and conditional compilation

Posted by jpluimers on 2020/10/15

There are various ways for Delphi code to verify what features are available.

Historically, testing for the existence of VER### defines with $IFDEF or $IFNDEF is the oldest means, and as of Delphi 6, you can also test for the existence and values of identifiers is $IF defined, $IF not defined and, especially for CompilerVersion and RTLVersion .

My versioned PowerShell script List-Delphi-Installed-Packages.ps1 tries to keep an up to date list of versions and features starting with BDS 1 (which was C# Builder) and BDS 2 (which was Delphi 8 with VER160). One day I will make it Pascal based in stead of BDS based.

The JEDI Code Library has kept a versioned JEDI.INC up to date since Delphi 1.0.

Binding those to specific features can be a tough thing when you depend on version numbers, but less hard when you rely on feature names.

Every couple of years, people start proposing units to replace include files, usually with an argument like this:

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

The problem is that for such a unit to work:

  1. you have to always use it (debunking the above argument)
  2. you will need to have the very latest version, even if you use old compilers (so you can use code written for any Delphi version)
    • See the quoted below “Do you expect that the old versions will get an update to know the new constants RTLVersion_Atlantis = 99.0; too?” below
  3. you need a central place where that version is available (like JEDI.INC)
  4. it will only work with booleans
    • See the quote below “Which is also backwards compatible because the compiler (at least in XE and up, haven’t checked any older versions) just evaluates a non existing value as False.”
  5. it requires $IF, which is a pain in the ass
    • See the quote below “Using $IF is a major pita because of $IFEND or $ENDIF depending on compiler version and $LEGACYIFEND setting.”

The only good thing is what Rudy Velthuis commented:

checking for a $DEFINE like DELPHI_RIO_UP can go wrong. If you have a typo, it will simply not be recognized as defined and compile the wrong code. Checking for {$IF CompilerVersion >= some_constant} will fail to compile if some_constant is not defined

A few people tried:

JEDI.INC

You might think that JEDI.INC was only introduced in 2003, but it is in fact much older as the JEDI Code Library had its own version control system (initially called FreeVCS) before first switching to SVN and later to GIT.

So these are only part of the history:

VER### got introduced as VER40 in Turbo Pascal 4

The product naming mess now completely has disconnected people from binding it to their Delphi version.

It helps knowing that VER### is the compiler version starting with Turbo Pascal 1, and remembering that Delphi 1 had VER80, and the three digits only started with Delphi 3 which introduced VER100.

Turbo Pascal 1 through 3 did not have any VER## defined, despite some sites

The first ever VER## conditional define was VER40 was introduced in Turbo Pascal 4, as you can see in Full text of “borland :: turbo pascal :: Turbo Pascal Version 4.0 Owners Manual 1987” (or PDF via borland :: turbo pascal :: Turbo Pascal Version 4.0 Owners Manual 1987 : Free Download, Borrow, and Streaming : Internet Archive)

Related:

JEDI History:

–jeroen

[WayBack] http://www.uweraabe.de/Blog/2016/01/22/on-conditional-compilation/

Oliver Münzberg

How will this be helpful baking these constants into the System.pas?

Do you expect that the old versions will get an update to know the new constants RTLVersion_Atlantis = 99.0; too?

{$IF RTLVersion < RTLVersion_Atlantis}
  Result := nil;
{$ELSE}
  Result := TBrainReader.Create( User );
{$ENDIF}

This will not compile unless you define the constant.

There should be an official VersionConst.inc/VersionConst.pas (simply including the inc) managed by EMBA and published through a VCS you can link to.

That would be helpful.

Stefan Glienke

What’s wrong with using jedi.inc?

There is nothing wrong …

{$IF CompilerVersion >= CompilerVersion_XE2}

is just more readable to me than

{$IFDEF DELPHI16_UP}

I know DELPHI16_UP includes also we are for sure dealing with Delphi

+Uwe Raabe https://github.com/project-jedi/jedi?files=1 has files exposing both version and feature based defines. The main file jedi.inc is for Delphi and FPC. A smaller file is for Kylix only. It’s maintained well.
Actually, the feature based conditionals are much more usable that the version based ones. The programmer shouldn’t have to worry about which language feature or library was introduced with which Delphi version. That can be solved once.
+Jeroen Wiert Pluimers And a feature like `record helper` doesn’t tell about the existence of TStringHelper or TIntegerHelper, let alone the level of implementation (like string.ToHexString f.i). There may be cases when such a feature define is sufficient, but I doubt it will cover the majority of needs.
+Uwe Raabe can you do `if defined(TStringHelper)` ?
+Jeroen Wiert Pluimers Probably not. Seems to return `false` in Seattle while not Defined succeeds.
+Nicholas Ring Both indeed – it should be added as well as it would not solve that issue. Actually I don’t see that much benefit in these feature defines, which might just be caused by my workflow. When I implement a new feature or extend or refactor existing code I usually code for the latest version. Then I step down to each previous version and react when the compiler complains. Therefore I can easily spot the affected version to introduce a condition. Because I mostly go for RTLVersion a simple Ctrl-Click reveals the number to check for. It’s plain easy and I rarely (never?) have the need for an include file.

Using $IF is a major pita because of $IFEND or $ENDIF depending on compiler version and $LEGACYIFEND setting.

+Uwe Raabe The compiler versions are wrong. They are being counted since TP and thus Seattle is 30. This means unless EMBT decides to progress the RTL independently from the compiler and change the way they are increasing these numbers both are exactly the same.

+Oliver Münzberg How is

{$IF CompilerVersion >= CompilerVersion_XE2}

any more readable than

{$IFDEF DelphiXE2_UP}

So if you want to do the $IF approach then get rid with the version checking yourself and declare these consts as following:

const
DelphiXE = CompilerVersion = 22;
DelphiXE2 = CompilerVersion = 23;
DelphiXE3 = CompilerVersion = 24;
DelphiXE4 = CompilerVersion = 25;
DelphiXE5 = CompilerVersion = 26;
DelphiXE6 = CompilerVersion = 27;
DelphiXE7 = CompilerVersion = 28;
DelphiXE8 = CompilerVersion = 29;
Delphi10Seattle = CompilerVersion = 30;

DelphiXEOrGreater = CompilerVersion >= 22;
DelphiXE2OrGreater = CompilerVersion >= 23;
DelphiXE3OrGreater = CompilerVersion >= 24;
DelphiXE4OrGreater = CompilerVersion >= 25;
DelphiXE5OrGreater = CompilerVersion >= 26;
DelphiXE6OrGreater = CompilerVersion >= 27;
DelphiXE7OrGreater = CompilerVersion >= 28;
DelphiXE8OrGreater = CompilerVersion >= 29;
Delphi10SeattleOrGreater = CompilerVersion >= 30;

Now you can just write:

{$IF DelphiXE3OrGreater}
result := S.ToUpper;
{$ELSE}
result := Uppercase(S);
{$IFEND}

Which is also backwards compatible because the compiler (at least in XE and up, haven’t checked any older versions) just evaluates a non existing value as False.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.