views:

275

answers:

4

Let's say I have a record TQuaternion and a record TVector. Quaternions have some methods with TVector parameters. On the other hand, TVector supports some operations that have TQuaternion parameters.

Knowing that Delphi (Win32) does not allow for forward record declarations, how do I solve this elegantly?

Using classes is not really an option here, because I really want to use operator overloading for this rare case where it actually makes good sense.

For now I simply moved these particular methods out of the records and into separate functions, the good old-fashioned way. Better suggestions are most welcome.

+1  A: 

I think that your "good old-fashioned" solution is the better.
as an alternate solution - but leading to your solution's schema - what about a Record containing your two record and the methods as methods of the main Record:

type  
  TQuaternionVector = record  
    Vector: TVector;  
    Quaternion: TQuaternion;  
    procedure F(V: TVector; Q: TQuaternion);    
  end;
loursonwinny
How are the two fields related to the two parameters in this example? Perhaps some funky results can be achieved with this approach if you combine it with implicit assignments, but otherwise I don't think it is very practical. :)
Paul-Jan
+1  A: 

For the specific case of wanting to use operator overloading (and even more specifically, binary operators), the record types being operated on can be specified in either order.

The declarations below will allow you to add vectors and quaternions (if that makes any sense :-) ) in any combination (the 3rd Add declaration for TQuaternion is the interesting one):

type
  TVector = record
    class operator Add( v1, v2 : TVector ) : TVector;
    end;

  TQuaternion = record
    class operator Add( q1, q2 : TQuaternion ) : TQuaternion;
    class operator Add( q : TQuaternion; v : TVector ) : TQuaternion;
    class operator Add( v : TVector; q : TQuaternion ) : TQuaternion;
    end;

Assuming appropriate variable declarations the following all compile:

q1 := q2 + q3;
v1 := v2 + v3;

q1 := q2+v2;
q2 := v2+q2;

Is this enough to cover what you need?

Alistair Ward
Insightful, but unfortunately the method at hand is _not_ an operator (I just mentioned the class operators to motivate my choice of sticking with records).
Paul-Jan
+3  A: 

There is no elegant solution in general, only old hacks with untyped references like this:

type
  TVector = record
    procedure UseQuaternion(var Q);
  end;

  TQuaternion = record
    procedure UseVector(var V: TVector);
  end;

{ TVector }

procedure TVector.UseQuaternion(var Q);
var
  Quaternion: TQuaternion absolute Q;

begin
  Quaternion.UseVector(Self);
end;

{ TQuaternion }

procedure TQuaternion.UseVector(var V: TVector);
begin

end;

For the particular cases the following pattern may be useful:

type
  TVectorData = array [0..1] of Double;
  TQuaternionData = array [0..3] of Double;

  TVector = record
    Data: TVectorData;
    procedure UseQuaternion(var Q: TQuaternionData);
  end;

  TQuaternion = record
    Data: TQuaternionData;
    procedure UseVector(var V: TVector);
  end;
Serg
+3  A: 

If the operators are not the actual problem you can solve this using a record helper.

type
  TVector = record
  end;

  TQuaternion = record
    procedure UseVector(var V: TVector);
  end;

  TVectorHelper = record helper for TVector
    procedure UseQuaternion(var Q: TQuaternion);
  end;

This will solve only the circular dependencies and it does not work with operators. It also has the drawback that you cannot have any other record helper for TVector, at least not both of them can be available in the same place.

Uwe Raabe
I wasn't aware there was a class helper equivalent for records, thanks! Although I don't think I'd actually _use_ this technique (I prefer to reserve helpers as a last resort to solve issues with code I cannot touch), it is the most direct answer to my question, and I'm marking it as such.
Paul-Jan
I wouldn't use that solution either for the same reasons. But you may consider changing the global functions into (static) class functions to narrow the scope.
Uwe Raabe