Elements IFDEF issues

I am working on updating about 36K lines of source code that, at one time, compiled to Delphi, FPC, and Oxygene. Here are the issues that I have not been able to write around after over 100 hours of editing to allow compiling under Delphi & Oxygene using the current $IFDEF pre-processing regime:

Generic list constraints
{$IFDEF OXYGENE}
DCustomBossNodeList = public class (TBeejObjectList) where T is DBossNode;
{$ELSE}
DCustomBossNodeList<T: DBossNode> = class (TBeejObjectList); // Oxygene throws error
{$ENDIF}

enum
This works, but causes way too much duplication (and I have some really large enums):
{$IFDEF OXYGENE}
TNwRuntimeDataSortMethod = public enum (
smMostInfluential,
smAsCollected
);
{$ELSE}
TNwRuntimeDataSortMethod = (
smMostInfluential,
smAsCollected
);
{$ENDIF}

Would prefer something like (and would be way more maintainable):

TNwRuntimeDataSortMethod = {$IFDEF OXYGENE}public enum{$ENDIF} (
smMostInfluential,
smAsCollected
);

Class destructors
class destructor Destroy; // error now

Class helpers
TBeejStreamHelper = class helper for TStream // error now

Variant type
var v: Variant; // error now

Array of const
function FormattedTranslation(const s: UnicodeString; const aParams: Array of const): UnicodeString; // Array of const is error now

Initialization & Finalization sections
Not allowed now. Either need these or class destructor, preferrably both.

I’ll log to see if we can actually allow those syntaxes in Delphi Compatibility Mode, making the ifdefs unnecessary. IFDEFs do need to surround full scopes, as Oxygene ifdef processing is not just a simple macro pre-processor, there actually become part of the syntax tree.

This should work in COmpatibility mode.

This I don’t think we’ll support; extensions are what Oxygene sues for a similar concept but (as limited as I;m familiar with class helpers) they work different enough so that it probably won’t be easy to support class helpers as syntax, even in compatibility mode. But ill check with the compiler team.

Variants are not something thats supported, but you can probably/possibly create a custom Variant type that emulates what you need (depending on how/what-for you use variants).

Neve heard of this. what does this do? I can see if we can support this in Compatibility Mode.

We do support these in Compatibility Mode, on there Island platform. On .NET (which I’m assuming is your target?), this feature cannot be supported due to runtime limitations.

Thanks, logged as bugs://83657 (generic constraints syntax)

Thanks, logged as bugs://83658 (enum syntax)

The issues I mentioned are in blocks that are IFDEF’d out under Oxygene and are only expected to compile under Delphi. I would hate to invoke Delphi Compatibility mode when it is for code not targeted to Oxygene. So what I was hoping for was leniency in the parsing of non-Oxygene blocks.

I am targeting .NET Core & WPF, but will also be targeting WASM.

The way IFDEFs work in Oxygene 10, the entire code must be a valid syntax tree. you can have undefined/un known identifiers in the out0-defined code, but it must be syntacticly valid Oxygene code. Thats because IFDE is not a simple pre-processor that run on the plain text before it goes into the compiler; the ifdefs were part of the syntax tree, which enables more sophisticated use case such as if defined() as part of regular code expressions, as well as a shared tree for cross-platform meta-code libraries (“Gotham” libraries).

See https://blogs.remobjects.com/2017/12/21/ifdef-redefined/ for more details.

Understood. That’s why I spent all those hours editing the source code before posting.

The biggest issues for me are the generic constraints syntax and class destructors (turning on Delphi compatibility did not make it allowed and caused other issues). Enum would be nice.

I’ve already moved some code out into separate units where the code wasn’t shared as tightly and am resisting moving code out due to declaration differences.

BTW, I found a kludge for the Variant issue:

{$IFDEF SOMETHING_NEVER_DEFINED}
type Variant = String;
{$ENDIF}
It’s an ugly solution but it allows declaring a Variant in Delphi when needed.

Yeah, im sure we can support these.

Curious — what error did yo get before you added that? var v: Variant; should have been fine in out-ifdef’ed code, as Variant would be just an unknown type…

It was “unknown type”. But moving it from the var section (and IFDEF’d out) of the method to in-block also resolves the issue.

Thanks for your quick responses and effort to help!

Hm, so thats in code thats active for Oxygene or not? and declaring the dummy alias in inactive code fixed the issue?

Yes, inactive for Oxygene and dummy alias in inactive code. But I’ve also noticed strange things related to position in the source code, especially in var sections and uses clauses. Sometimes just bumping a line of code up or down a line or two makes a difference in whether the compiler accepts it or not. I am using both Fire & Water. If I can replicate again I will post examples.

Bruce,

That’d be appreciated, as that definitely sounds like something that needs fixing.

Do you have a test case for the Variant issue? This:

namespace ConsoleApplication451;

type
  Program = class
  public

    class method Main(args: array of String): Int32;
    begin
      // add your own code here
      writeLn('The magic happens here.');
    end;
    
    {$IFDEF NOT_OXYGENE}
    var x: Variant;
    {$ENDIF}

  end;

end.

predictably, compiles fine for me, so there must be more to it. You should not be getting “Unknown Type” or “Unknown Identifier” in inactive code.

bugs://83658 got closed with status fixed.

bugs://83657 got closed with status fixed.

I think I figured out when and why I was getting the error when using Variant. The only way I have been able to replicate the error is shown below where the line does not include “var”. I am guessing the compiler doesn’t keep track that it is in the var section of the method and needs the var to make the line “whole”.

// throws E28
procedure TDbTableInfo.SyncWithMaster;
var
s: String;
{$IFNDEF OXYGENE}
v: Variant; // E28 Unknown type “Variant”
{$ENDIF !OXYGENE}
begin
// irrelevant code…
end;

// works
procedure TDbTableInfo.SyncWithMaster;
var
s: String;
{$IFNDEF OXYGENE}
var v: Variant;
{$ENDIF !OXYGENE}
begin
// irrelevant code…
end;

Reproduced…

Thanks, logged as bugs://83694

This code throws a hint, but no error (I have 60+ instances of this hint):

procedure DKnowledgeBase.CollectTopics(topics: TStrings);
var
topic: DBossNode;
{$IFDEF NEEDS_ITERATOR_DECLARATION}
key: Integer; // H10 Local variable “key” is not used
{$ENDIF}
begin
// can’t use fTopicsByName because the keys (names) are pushed to lowercase
for key in fTopicsByID.Keys do
begin
topic := fTopicsByID[key];
topics.AddObject(topic.GetName, topic);
end;
end;

This does not:

procedure DKnowledgeBase.CollectTopics(topics: TStrings);
var
topic: DBossNode;
{$IFDEF NEEDS_ITERATOR_DECLARATION}
var key: Integer;
{$ENDIF}
begin
// can’t use fTopicsByName because the keys (names) are pushed to lowercase
for key in fTopicsByID.Keys do
begin
topic := fTopicsByID[key];
topics.AddObject(topic.GetName, topic);
end;
end;

Probably same issue; I’ll amend.

bugs://83694 got closed with status fixed.