using and ab-using ifdef

I am currently trying to update an open source project from Delphi 2007 to XE2 and found some code which supposedly already works for XE2. But it is ab-using ifdef in several ways so I thought I’d blog about this to vent some steam and also possibly educate others.
(Class name changed to protect the innocents. ;-))

Please, never ever do this:

function TSomething.GetRecord(Buffer: {$IFDEF DELPHI_2009} RecordBuffer{$ELSE}PChar{$ENDIF}; GetMode: TGetMode; DoCheck: Boolean): TGetResult;

There are actually several things wrong with this code:

  1. Never use an ifdef for a specific Delphi version. Instead define a symbol for a specific feature like
    {$ifdef DELPHI_2009}
      {$define SUPPORTS_TRECORDBUFFER}
    {$endif}
    

    and use that one in code. Remember: Usually, new features stay with all later Delphi versions, so even the name of the conditional is wrong, it should be DELPHI_2009_AND_UP or similar, if used in that way.

  2. Don’t sprinkle ifdefs like this through 1000s of lines of code just because the type of a variable, parameter or function result has changed. Instead define your own type like this:
    type
    {$ifdef SUPPORTS_TRECORDBUFFER}
      TMyRecordBuffer = TRecordBuffer;
    {$else}
      TMyRecordBuffer = PChar;
    {$endif}
    

    and use it wherever necessary. It makes the code so much more readable.

  3. At least they got something right: They don’t use VERxxx all over the place but have an include file as a central place to check for compiler versions.
    But: Don’t write your own version include file, instead use one from a popular open source project like Project Jedi when possible. There used to be a compilers.inc file on the interwebs, but I don’t think it is being maintained any more.

EDIT: There is DelphiVersions.inc in the Delphi Wiki, which has been updated by me and occasionally somebody else up to Delphi 11 (which is the current version now).
— 2023-04-08

</rant>