Why doesn't Delphi 7 allow a TList of Extended type

647 Views Asked by At

I've created several simple lists (and integer list and color list) yet when I try to make an "extended" list it says invalid typecast even though I've used similar typecasting for the prior 2 lists (it throws the error wherever I use the Extended() typecast).

Type
  TAExtList = Class(TObject)
  Private
    FList: TList;
    Procedure SetExt(Index: Integer; Value: Extended);
    Function GetCnt: Integer;
    Function GetExt(Index: Integer): Extended;
  Public
    Constructor Create;
    Destructor Destroy; Override;
    Function Add(Value: Extended): Integer;
    Function Insert(Index: Integer; Value: Extended): Integer;
    Procedure Delete(Index: Integer);
    Procedure Clear;
    Function IndexOf(Value: Extended): Integer;
    Property Count: Integer Read GetCnt;
    Property Extendeds[Index: Integer]: Extended Read GetExt Write SetExt; Default;
  End;

Function TAExtList.Add(Value: Extended): Integer;
Begin
  Result := FList.Add(Pointer(Value));
End;
2

There are 2 best solutions below

8
Rudy Velthuis On BEST ANSWER

There are several reasons. First, as MBo already wrote in his answer, an Extended is 10 bytes in size, so it doesn't fit in the 32 bits (4 bytes) of a Pointer.

Another reason is that in Delphi, direct hard casts to floating point types are not allowed, because too many C programmers expected the cast to do a conversion, instead of just a reinterpretation of the bytes of the Extended (or other floating point type). Although the sizes would match, casting to Single is not allowed either. Very early versions of Delphi did actually allow these casts, if I remember correctly (or was that Turbo Pascal?).

But you can still create your TExtendedList, if you use pointers to Extended (after all, a TList stores pointers):

type
  PExtended = ^Extended;

function TExtendedList.Add(const E: Extended): Integer;
var
  P: PExtended;
begin
  New(P);       // allocate an Extended on the heap and "return" a pointer to it
  P^ := E;      // store the parameter on the heap
  inherited Add(P); // add the pointer to the list
end;

But this means that your TList now contains pointers to Extendeds that are allocated on the heap. So removing or changing requires you to use

Dispose(P);

at the appropriate places (e.g. in Delete(), Remove(), Extract(), Clear, etc. and the destructor). Each of the allocated Extendeds must be disposed of at the right time.

Retrieving is similar:

function TExtendedList.GetExt(Index: Integer): Extended;
var
  P: PExtended;
begin
  P := inherited Items[Index]; 
  Result := P^;
  // or short form: Result := PExtended(inherited Items[Index])^;
end;

procedure TExtendedList.SetExt(Index: Integer; Value: Extended);
var
  P: PExtended;
begin
  P := inherited Items[Index];
  P^ := Value;
  // or short form: PExtended(inherited Items[Index])^ := Value;
end;

procedure TExtendedList.Delete(Index: Integer);
begin
  Dispose(PExtended(inherited Items[Index]));
  inherited Delete(Index);
end;

procedure TExtendedList.Clear;
var
  I: Integer;
begin
  for I := 0 to Count - 1 do
    Dispose(PExtended(inherited Items[I]));
  inherited Clear;
end;    

Etc., etc. ...

Update

As @kobik said, you could use the virtual Notify function to delete the items that are removed, instead of in each method:

procedure TExtendedList.Notify(Ptr: Pointer; Action: TListNotification); // declare as override;
begin
  inherited;
  if Action = lnDeleted then
    Dispose(PExtended(Ptr));
end;
0
MBo On

Both Integer and TColor have the same size (4 bytes) as Pointer in Delphi 7, that is why explicit casting is possible.

docWiki:

Variable Typecasts
You can cast any variable to any type, provided their sizes are the same and you do not mix integers with reals.

But Extended is real, its size is 10 bytes and you cannot cast it to Pointer. Moreover, there is no enough place for it.

P.S. Note that fresh Delphi versions contain rather convenient instrument - generics - just define and create TList<Extended>.