delphicastingtlist

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


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;

Solution

  • 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;