3

Is it possible to create a conditional define like so:

{$if typeof(TNode) = record}
type PNode = ^TNode;
{$else}
type PNode = TNode;
{$end}

Why do I want this?
I'm alternating between using class and record for a specific problem.
I want to use record for speed reasons, but also want to use class for convenience.

For this reason I'm switching between the two.
Obviously I can add a {$define} statement, but it would be nice to be able to automate this.

Johan
  • 74,508
  • 24
  • 191
  • 319
  • I would use the {$define} method anyway because I would be wanting to switch the record/class definition at the same time. It just seems simpler to me. – Dsm Jun 09 '17 at 08:22
  • No, it is not possible, as far as I know. But I'd love to be proven wrong. – Rudy Velthuis Jun 09 '17 at 08:59
  • 1
    How could this possibly work, anyway? `PNode^.SomeField` is valid if it is a record pointer but would be nonsensical for a class reference. Surely this is not a generally valid substitution. – J... Jun 09 '17 at 09:11
  • 1
    @J..., in case of a record pointer you can also write `PNode.SomeField` which compiles also if PNode is a class. – Uwe Raabe Jun 09 '17 at 09:27
  • What kind of convenience are you missing in records? You can't possibly mean inheritance and the like? – Uli Gerhardt Jun 09 '17 at 09:45
  • @UweRaabe It was one example. Record semantics are quite different in many other regards. Instantiation, assignment, lifetime management, etc, are all different. I feel a `{$define}` must be necessary for other areas besides simply the type declaration. Either that or the record type would need to be designed and used in convoluted, non-standard, and confusing ways that would certainly hurt readability and maintainability. – J... Jun 09 '17 at 09:46
  • @J... Delphi allows you to drop the `^` when dereferencing pointers. So `PNode.SomeField` works perfectly fine when `type PNode = ^TNode` – Johan Jun 09 '17 at 10:53
  • You can define the record constructor as `class function Create: TNodeAsRect`. and leave the destructor as an empty inline function. The convienence I'm missing is polymorphism. I guess I could use old skool objects. – Johan Jun 09 '17 at 10:56
  • Delphi has an `{$IF DECLARED(...)}` directive, I wonder if it works on class members? I haven't tried it yet, but maybe it can test for the existence of `TObject` methods, like `{IF DECLARED(TNode.ClassType)}` – Remy Lebeau Jun 09 '17 at 14:22

2 Answers2

3

Although I personally recommend the general DEFINE approach, you might be successful in those cases where the record is not of a specific size:

{$if Sizeof(TNode) <> Sizeof(Pointer)}
type PNode = ^TNode;
{$else}
type PNode = TNode;
{$end}

OK, I know that is dirty programming, but you asked for it in the first place.

Uwe Raabe
  • 45,288
  • 3
  • 82
  • 130
  • 3
    Note this is not a general solution, but very specialized. In the general sense, this fails if the record contains members whose sizes *happen* to add up to the size of a pointer. For instance, if the record contains a single `Integer` in 32bit or a single `Int64` in 64bit. Or a single `NativeInt`. Or any other combination of types that adds up to 4 or 8 bytes. – Remy Lebeau Jun 09 '17 at 14:19
1

If you control both definition of TNode, you could do it like this (Does not need to be in same unit, but must reference the same constant) :

const
  NODE_IS_RECORD = False;

type
{$if NODE_IS_RECORD}
  TNode = record

  end;
  PNode = ^TNode;
{$ELSE}
  TNode = class

  end;
  PNode = TNode;
{$IFEND}

If you control only 1 declaration of TNode, you could still do it like this :

{Unit1}
type
  TNode = record

  end;
  PNode = ^TNode;

{Unit2}
{$IF not DECLARED(PNode)}
  //if you don't use the unit where TNode is a record, then PNode shouldn't be declared.
  PNode = TNode;
{$ENDIF}

If you control neither declaration, but they are declared in different units(Actually, I think it's required...) and you never use both, or using both always means you want to use a specific declaration of PNode :

{$IF DECLARED(UnitContainingTNodeAsRecord)}
  PNode = ^TNode;
{$ELSE}
  PNode = TNode;
{$IFEND}

You might want to prefix TNode with the name of the unit if you have both unit in the uses. "DECLARED" only ensure it is declared, not that it's the "closest" in scope.

I think that covers most situations.

Ken Bourassa
  • 6,363
  • 1
  • 19
  • 28