14

I am using the operator overloading for records in Delphi 2006. (Please don't answer this question by telling me not to.)

I have two record types with the implicit operator overloaded. They are both only in the implementation of the module, not exposed through the interface.

My problem is, now that they are mutually dependent, I don't know how to forward declare the second type to the compiler. I know how to do this with functions, procedures, and classes, but not with records.

Here is a simplified example of what I am trying to do:

implementation

type
  TMyRec1 = record
    Field1 : Integer;
    class operator Implicit(a: TMyRec2): TMyRec1;  // <---- Undeclared Identifier here.
  end;

  TMyRec2 = record
    Field2: Integer;
    class operator Implicit(a: TMyRec1): TMyRec2;
  end;

class operator TMyRec1.Implicit(a:TMyRec2): TMyRec1;
begin
  Result.Field1 := a.Field2;
end;

class operator TMyRec2.Implicit(a:TMyRec2): TMyRec2;
begin
  Result.Field2 := a.Field1;
end;
Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
Richard A
  • 2,783
  • 2
  • 22
  • 34
  • If each type can implicitly convert itself to the other type, you might want to reconsider whether you really need two separate types. – Rob Kennedy Apr 21 '09 at 02:44
  • @Rob: It's quite possible. I recently ran into this same situation when I had two different vector types, one made of integers and one of floats. You need both types for different things, but you want the two to be mutually assignment-compatible. The solution was basically the one you posted. – Mason Wheeler Apr 21 '09 at 02:51
  • 2
    The types are nowhere this simple. One carries summary statistics about the other. They are used to logically group data that is passed around elsewhere. [reposted to try to get rid of excess white space.] – Richard A Apr 21 '09 at 02:56

2 Answers2

14

You can't have forward declarations for record types. Define both Implicit operators in the second type:

type
  TMyRec1 = record
    Field1 : Integer;
  end;

  TMyRec2 = record
    Field2: Integer;
    class operator Implicit(a: TMyRec2): TMyRec1;
    class operator Implicit(a: TMyRec1): TMyRec2;
  end;

Quoting from the help:

Implicit conversions should be provided only where absolutely necessary, and reflexivity should be avoided. It is best to let type B implicitly convert itself to type A, and let type A have no knowledge of type B (or vice versa).

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • 1
    Thanks Rob. Obvious solution I guess, but they all are when you know. Why do you say that implicit conversion should be avoided? – Richard A Apr 21 '09 at 02:48
  • 1
    *I* didn't say it should be avoided. I was quoting the documentation. But since you asked, implicit conversion can make code harder to read; it's harder to notice a conversion occurring when there's nothing calling any attention to it. I'd just use an ordinary function to convert from one type to another. Or you could use operator Explicit instead. – Rob Kennedy Apr 21 '09 at 03:12
  • Thanks. I should read more carefully. I skimmed the bit that says 'quoting from the help'. Yes, I'm a bit nervous about operator overloading and implicit conversions too, but trying it out in a small case to see how it goes. Funnily, some major performance problems I have had to fix have been with implicit conversion of standard numeric and string types. – Richard A Apr 21 '09 at 05:04
3

You might be able to do this with record helpers.

Below is what I recently did to work around the impossibility of having a forward record record declaration.

It uses the record helper construct, which - like implicit type casts - have drawbacks too.
The most important one being that only the nearest record helper for a particular record type will apply.

type
  TIpv4Address = record
  strict private
    FAddress: TIpv4Quad;
    FMask: TIpv4Quad;
  private
    class function CreateInternal(const IP_ADDR_STRING: _IP_ADDR_STRING): TIpv4Address; static;
  public
    class function Create(const IP_ADDR_STRING: _IP_ADDR_STRING): TIpv4Address; static;
    class function Count(const IP_ADDR_STRING: _IP_ADDR_STRING): Integer; static;
    property Address: TIpv4Quad read FAddress;
    property Mask: TIpv4Quad read FMask;
  end;

  TIpv4AddressList = array of TIpv4Address;

  TIpv4AddressHelper = record helper for TIpv4Address
    class function CreateList(const IP_ADDR_STRING: _IP_ADDR_STRING): TIpv4AddressList; static;
  end;

You use it like this:

function TAdapterInfo.GetIpAddressList: TIpv4AddressList;
begin
  Result := TIpv4Address.CreateList(AdapterInfos.IP_ADAPTER_INFO[Index].IpAddressList);
end;

--jeroen

Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
  • Thanks. I'll take a look. I wasn't aware of teh 'record helper' construct. – Richard A Jan 20 '11 at 23:55
  • 1
    It got silently introduced in Delphi 2006 for both Win32 and .NET. Class helpers were introduced in Delphi 2005 for Win32 and the first Delphi .NET version (a version number of which many people do not want to be reminded). – Jeroen Wiert Pluimers Jan 21 '11 at 13:23