Problem with typecast in Delphi XE - delphi

I try to do list of procedures this way:
type
TProc = procedure of object;
TMyClass=class
private
fList:Tlist;
function getItem(index:integer):TProc;
{....}
public
{....}
end;
implementation
{....}
function TMyClass.getItem(index: Integer): TProc;
begin
Result:= TProc(flist[index]);// <--- error is here!
end;
{....}
end.
and get error:
E2089 Invalid typecast
How can I fix it?
As I see, I can make a fake class with only one property Proc:TProc; and make list of it. But I feel that it's a bad way, isn't it?
PS: project have to be delphi-7-compatible.

The typecast is invalid because you can not fit a method pointer to a pointer, a method pointer is in fact two pointers first being the address of the method and the second being a reference to the object that the method belongs. See Procedural Types in the documentation. This will not work in any version of Delphi.

Sertac has explained why your code doesn't work. In order to implement a list of such things in Delphi 7 you can do something like this.
type
PProc = ^TProc;
TProc = procedure of object;
TProcList = class(TList)
private
FList: TList;
function GetCount: Integer;
function GetItem(Index: Integer): TProc;
procedure SetItem(Index: Integer; const Item: TProc);
public
constructor Create;
destructor Destroy; override;
property Count: Integer read GetCount;
property Items[Index: Integer]: TProc read GetItem write SetItem; default;
function Add(const Item: TProc): Integer;
procedure Delete(Index: Integer);
procedure Clear;
end;
type
TProcListContainer = class(TList)
protected
procedure Notify(Ptr: Pointer; Action: TListNotification); override;
end;
procedure TProcListContainer.Notify(Ptr: Pointer; Action: TListNotification);
begin
inherited;
case Action of
lnDeleted:
Dispose(Ptr);
end;
end;
constructor TProcList.Create;
begin
inherited;
FList := TProcListContainer.Create;
end;
destructor TProcList.Destroy;
begin
FList.Free;
inherited;
end;
function TProcList.GetCount: Integer;
begin
Result := FList.Count;
end;
function TProcList.GetItem(Index: Integer): TProc;
begin
Result := PProc(FList[Index])^;
end;
procedure TProcList.SetItem(Index: Integer; const Item: TProc);
var
P: PProc;
begin
New(P);
P^ := Item;
FList[Index] := P;
end;
function TProcList.Add(const Item: TProc): Integer;
var
P: PProc;
begin
New(P);
P^ := Item;
Result := FList.Add(P);
end;
procedure TProcList.Delete(Index: Integer);
begin
FList.Delete(Index);
end;
procedure TProcList.Clear;
begin
FList.Clear;
end;
Disclaimer: completely untested code, use at your own risk.

Related

use Delphi to read in a text file to a TStringList but bottom to top

I was looking to read a text file in reverse so it would read in from the bottom of the text file first. I did find how to reverse it but it doesn't make sense to me. Can someone explain this to me, how it's working? Also if there is a better/quicker way? It seems to do all the work after the file is read in, like it would be quicker to just read it in from the bottom.
var
datalist : TStringList;
lines,i : Integer;
saveLine : String;
begin
datalist := TStringList.Create;
datalist.LoadFromFile(filename); //loads file
lines := datalist.Count;
for i := lines-1 downto (lines div 2) do
begin
saveLine := datalist[lines-i-1];
datalist[lines-i-1] := datalist[i];
datalist[i] := saveLine;
end;
(At least in Delphi 7, but more recent versions should act similarily)
.LoadFromFile() calls
.LoadFromStream(), which reads the whole stream/file into memory and then calls
.SetTextStr(), which just calls per line
.Add()
Knowing this helps us to avoiding to reinvent the whole wheel and instead using an own class with one subtle change in the .Add() method:
type
TStringListReverse= class( TStringList )
function Add( const S: String ): Integer; override;
end;
function TStringListReverse.Add( const S: String ): Integer;
begin
Result:= {GetCount} 0; // Our change: always in front
Insert( Result, S );
end;
And now we just use our own class:
var
l: TStringListReverse;
begin
l:= TStringListReverse.Create;
l.LoadFromFile( 'C:\Windows\win.ini' );
Memo1.Lines.Assign( l );
l.Free;
As I mentioned in a comment, it might be useful to create an adapter class that accepts a TStrings instance, and exposes it as another TStrings, but reversed.
This might look like this:
type
TReversedStrings = class(TStrings)
private
FSource: TStrings;
FOwnsSource: Boolean;
function ReversedIndex(Index: Integer): Integer;
protected
procedure Put(Index: Integer; const S: string); override;
function Get(Index: Integer): string; override;
function GetCount: Integer; override;
function GetObject(Index: Integer): TObject; override;
procedure PutObject(Index: Integer; AObject: TObject); override;
public
constructor Create(Source: TStrings; AssumeOwnership: Boolean);
destructor Destroy; override;
procedure Clear; override;
procedure Delete(Index: Integer); override;
procedure Exchange(Index1, Index2: Integer); override;
function IndexOf(const S: string): Integer; override;
procedure Insert(Index: Integer; const S: string); override;
procedure Move(CurIndex, NewIndex: Integer); override;
end;
{ TReversedStrings }
constructor TReversedStrings.Create(Source: TStrings; AssumeOwnership: Boolean);
begin
inherited Create;
FSource := Source;
FOwnsSource := AssumeOwnership;
end;
destructor TReversedStrings.Destroy;
begin
if FOwnsSource then
FSource.Free;
inherited;
end;
function TReversedStrings.ReversedIndex(Index: Integer): Integer;
begin
Result := FSource.Count - Index - 1;
end;
procedure TReversedStrings.Put(Index: Integer; const S: string);
begin
FSource[ReversedIndex(Index)] := S;
end;
function TReversedStrings.Get(Index: Integer): string;
begin
Result := FSource[ReversedIndex(Index)];
end;
function TReversedStrings.GetCount: Integer;
begin
Result := FSource.Count;
end;
function TReversedStrings.GetObject(Index: Integer): TObject;
begin
Result := FSource.Objects[ReversedIndex(Index)];
end;
procedure TReversedStrings.PutObject(Index: Integer; AObject: TObject);
begin
FSource.Objects[ReversedIndex(Index)] := AObject;
end;
procedure TReversedStrings.Clear;
begin
FSource.Clear;
end;
procedure TReversedStrings.Delete(Index: Integer);
begin
FSource.Delete(ReversedIndex(Index));
end;
procedure TReversedStrings.Exchange(Index1, Index2: Integer);
begin
FSource.Exchange(ReversedIndex(Index1), ReversedIndex(Index2));
end;
function TReversedStrings.IndexOf(const S: string): Integer;
begin
Result := FSource.IndexOf(S);
if Result > -1 then
Result := ReversedIndex(Result);
end;
procedure TReversedStrings.Insert(Index: Integer; const S: string);
begin
FSource.Insert(ReversedIndex(Index), S);
end;
procedure TReversedStrings.Move(CurIndex, NewIndex: Integer);
begin
FSource.Move(ReversedIndex(CurIndex), ReversedIndex(NewIndex));
end;
It should be obvious how to use this, and I've not tested the code, or even executed it. Consider it a sketch of an idea.
If you want to use the TStringList.LoadFromFile() function, then another way to do it is to copy one TStringList to another TStringList. It would be faster than the current scheme, and is fewer lines of code.
var
datalist1, datalist2 : TStringList;
lines, i: Integer;
filename : string;
begin
datalist1 := TStringList.Create;
datalist2 := TStringList.Create;
datalist1.LoadFromFile(filename); //loads file
lines := datalist1.Count;
data2list.Capacity := lines; // so it allocates the memory once
for i := lines-1 downto 0 do
begin
datalist2.Add (datalist1[i]);
end;
end;
Personally, I would read the file in myself.

Compiler not mapping a class method to an interface method

I am using Delphi Pro 10.2.3 Tokyo. I want to create a TDataset wrapper class which I can use to enumerate through a list of IData descendants with a for-in loop. When I try to compile the code below, I get the following error message.
[dcc32 Error] Core.Data.DatasetAdapter.pas(25): E2291 Missing implementation of interface method IEnumerator.GetCurrent
Clearly, GetCurrent is implemented. Any idea how to fix this?
unit Core.Data.DatasetAdapter;
interface
uses
Data.Db
;
type
IData = interface
['{15D1CF4F-B9E1-4525-B035-24B9A6584325}']
end;
IDataList<T: IData> = interface
['{9FEE9BB1-A983-4FEA-AEBF-4D3AF5219444}']
function GetCount: Integer;
function GetCurrent: T;
procedure Load;
procedure Unload;
property Count: Integer read GetCount;
property Current: T read GetCurrent;
end;
TDatasetAdapter<T: IData> = class(
TInterfacedObject
, IData, IDataList<T>
, IEnumerator<T>
)
private
FBof: Boolean;
FDataset: TDataset;
FIntf: T;
function GetCount: Integer;
function GetCurrent: T;
function GetEof: Boolean;
function GetInterface: T;
function MoveNext: Boolean;
procedure Reset;
protected
function FieldByName(const FieldName: string): TField;
procedure MapFields; virtual;
property Dataset: TDataset read FDataset;
public
constructor Create(ADataset: TDataset); virtual;
function GetEnumerator: IEnumerator<T>;
procedure Cancel;
procedure Close;
procedure Delete;
procedure Edit;
procedure First;
procedure Insert;
procedure Load;
procedure Next;
procedure Open;
procedure Post;
procedure UnLoad;
property Count: Integer read GetCount;
property Eof: Boolean read GetEof;
end;
implementation
uses
System.SysUtils
, System.TypInfo
;
{ TDatasetAdapter<T> }
{
****************************** TDatasetAdapter<T> ******************************
}
constructor TDatasetAdapter<T>.Create(ADataset: TDataset);
begin
FDataset := ADataset;
FIntf := GetInterface;
end;
procedure TDatasetAdapter<T>.Cancel;
begin
FDataset.Cancel;
end;
procedure TDatasetAdapter<T>.Close;
begin
FDataset.Close;
end;
procedure TDatasetAdapter<T>.Delete;
begin
FDataset.Delete;
end;
procedure TDatasetAdapter<T>.Edit;
begin
FDataset.Edit;
end;
function TDatasetAdapter<T>.FieldByName(const FieldName: string): TField;
begin
Result := FDataset.FieldByName(FieldName);
end;
procedure TDatasetAdapter<T>.First;
begin
FDataset.First;
end;
function TDatasetAdapter<T>.GetCount: Integer;
begin
Result := FDataset.RecordCount;
end;
function TDatasetAdapter<T>.GetCurrent: T;
begin
Result := FIntf;
end;
function TDatasetAdapter<T>.GetEnumerator: IEnumerator<T>;
begin
Reset;
Result := Self;
end;
function TDatasetAdapter<T>.GetEof: Boolean;
begin
Result := FDataset.Eof;
end;
function TDatasetAdapter<T>.GetInterface: T;
var
LGuid: TGuid;
begin
LGuid := GetTypeData(TypeInfo(T))^.Guid;
if not Supports(Self, LGuid, Result) then
Result := nil;
end;
procedure TDatasetAdapter<T>.Insert;
begin
FDataset.Insert;
end;
procedure TDatasetAdapter<T>.Load;
begin
Open;
MapFields;
end;
procedure TDatasetAdapter<T>.MapFields;
begin
//Stub procedure
end;
function TDatasetAdapter<T>.MoveNext: Boolean;
begin
if FBof then FBof := False
else Next;
Result := not Eof;
end;
procedure TDatasetAdapter<T>.Next;
begin
FDataset.Next;
end;
procedure TDatasetAdapter<T>.Open;
begin
FDataset.Open;
end;
procedure TDatasetAdapter<T>.Post;
begin
FDataset.Post;
end;
procedure TDatasetAdapter<T>.Reset;
begin
FBof := True;
First;
end;
procedure TDatasetAdapter<T>.UnLoad;
begin
Close;
end;
end.
You need to resolve function GetCurrent: T twice: for IDataList<T> and for Enumerator<T>. But you also need one for the non-generic ancestor of IEnumerator<T>: IEnumerator. Apparently that is not hidden by the GetCurrent method of IEnumerator<T>.
Try method resolution clauses:
function GetGenericCurrent: T; // implement this
function IDataList<T>.GetCurrent = GetGenericCurrent;
function IEnumerator<T>.GetCurrent = GetGenericCurrent;
function GetCurrent: TObject; // implement this -- can return nil.
The implementation of both can be the same, but you will have to make two methods. The one for the non-generic IEnumerator can return nil.
Update
I had to modify the code above. Now it should work. It is not necessary to have two implementations for GetCurrent returning T, but you must have one returning TObject.

Process to convert TObjectlist to TObjectList<T> to use in TObjectDataset

I would like to use TObjectDataset which relies on TObjectList<> (System.Generics.Collections / Spring.Collections) but only have a TObjectList (System.Contnrs). Is there any way besides for iterating through objects and building a new TObjectList<> to get this working? Ultimately I would like to couple the TObjectList to an Objectdataset in order to bind to an UI.
Your question is slightly wrong. The Spring4d TObjectDataSet takes an IObjectList interface which is a specialization of IList<T> where T is TObject.
This contract is matched by the Contnrs.TObjectList. So "simply" create a wrapper class for your TObjectList that implements IObjectList. I put simply in quotes because this interface has quite a lot of methods. You can use TListBase<T> as base class for your adapter which already has all methods implemented. Then you only need to override a few (take a look at TList<T> which ones those are).
One important detail to know is that the TObjectDataSet needs to know the exact class of the objects in your list. This is done via the ElementType property of the IObjectList. If that returns TObject though this is not very helpful. So you need to override that method.
Edit: Here is the full code of such an adapter class:
unit Spring.Collections.ObjectListAdapter;
interface
uses
Contnrs,
TypInfo,
Spring.Collections,
Spring.Collections.Base;
type
TObjectListAdapter = class(TListBase<TObject>, IObjectList)
private
fList: TObjectList;
fClassType: TClass;
protected
function GetCapacity: Integer; override;
function GetCount: Integer; override;
function GetElementType: PTypeInfo; override;
function GetItem(index: Integer): TObject; override;
procedure SetCapacity(value: Integer); override;
procedure SetItem(index: Integer; const value: TObject); override;
public
constructor Create(const list: TObjectList; classType: TClass);
procedure Delete(index: Integer); override;
function Extract(const item: TObject): TObject; override;
procedure Insert(index: Integer; const item: TObject); override;
procedure Exchange(index1, index2: Integer); override;
procedure Move(currentIndex, newIndex: Integer); override;
end;
implementation
uses
Classes,
Types;
{ TObjectListAdapter }
constructor TObjectListAdapter.Create(const list: TObjectList; classType: TClass);
begin
inherited Create;
fList := list;
fClassType := classType;
end;
procedure TObjectListAdapter.Delete(index: Integer);
begin
fList.Delete(index);
end;
procedure TObjectListAdapter.Exchange(index1, index2: Integer);
begin
fList.Exchange(index1, index2);
end;
function TObjectListAdapter.Extract(const item: TObject): TObject;
begin
Result := fList.Extract(item);
end;
function TObjectListAdapter.GetCapacity: Integer;
begin
Result := fList.Capacity;
end;
function TObjectListAdapter.GetCount: Integer;
begin
Result := fList.Count;
end;
function TObjectListAdapter.GetElementType: PTypeInfo;
begin
Result := fClassType.ClassInfo;
end;
function TObjectListAdapter.GetItem(index: Integer): TObject;
begin
Result := fList[index];
end;
procedure TObjectListAdapter.Insert(index: Integer; const item: TObject);
begin
fList.Insert(index, item);
end;
procedure TObjectListAdapter.Move(currentIndex, newIndex: Integer);
begin
fList.Move(currentIndex, newIndex);
end;
procedure TObjectListAdapter.SetCapacity(value: Integer);
begin
fList.Capacity := value;
end;
procedure TObjectListAdapter.SetItem(index: Integer; const value: TObject);
begin
fList[index] := value;
end;
end.
Is there any way besides for iterating through objects and building a new TObjectList<T> to get this working?
There is not. The two types are not compatible.

How to free a generic TList<TMyRecord> with generic sub lists in TMyRecord

In Delphi 10 Berlin under Windows I have the following question regarding the freeing of generic lists:
I have the following record/list structure:
type
TMyRecord=record
Value1: Real;
SubList1: TList<Integer>;
SubList2: TList<Real>;
end;
TMyListOfRecords=TList<TMyRecord>;
I want to free the structure with the following code:
var
i: Integer;
AMyListOfRecords: TMyListOfRecords;
begin
//other code
//free AMyListOfRecords and all its content
for i:=0 to AMyListOfRecords.Count-1 do
begin
AMyListOfRecords[i].SubList1.Free;
AMyListOfRecords[i].SubList2.Free;
end;
AMyListOfRecords.Free;
end;
This seems to work. But I am wondering if there is a simpler or more elegant solution?
You could transform record type to class - overhead is negligible because record already contains sub-objects. Free sub-objects in this class destructor, and use
TMyListOfClasses = TObjectList<TMyClass>;
with OwnsObjects = True
In this case all you need is
AMyListOfClasses.Free;
You can define the interfaced list for the sub-items like:
type
TMyRecord=record
Value1: Real;
SubList1: IList<Integer>;
SubList2: IList<Real>;
end;
TMyListOfRecords=TList<TMyRecord>;
Where IList is kind of:
type
IList<T> = interface
function Add(const AValue: T): Integer;
function Remove(AValue: T): Integer;
end;
where you implement it like this:
TIntfList<T> = class(TInterfacedObject, IList<T>)
private
FList: TList<T>;
function Add(const AValue: T): Integer;
function Remove(AValue: T): Integer;
constructor Create;
destructor Destroy; override;
end;
{ TIntfList<T> }
function TIntfList<T>.Add(const AValue: T): Integer;
begin
Result := FList.Add(AValue);
end;
constructor TIntfList<T>.Create;
begin
FList := TList<T>.Create;
end;
destructor TIntfList<T>.Destroy;
begin
FList.Free;
inherited;
end;
function TIntfList<T>.Remove(AValue: T): Integer;
begin
Result := FList.Remove(AValue);
end;
After that you can assign fields of your record with TIntfList.Create and they will be released automatically with your records.

Interfaces without reference counting

After reading many post on StackOverflow about the cons of using automatic reference counting for Interfaces, I started trying to manually reference counting each interface instantiation.
After trying for a full afternoon I give up!
Why do I get Access Violation when I call FreeAndNil(p)?
What follow is a complete listing of my simple unit.
unit fMainForm;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm4 = class(TForm)
btn1: TButton;
procedure FormCreate(Sender: TObject);
procedure btn1Click(Sender: TObject);
end;
type
IPersona = interface(IInterface)
['{44483AA7-2A22-41E6-BA98-F3380184ACD7}']
function GetNome: string;
procedure SetNome(const Value: string);
property Nome: string read GetNome write SetNome;
end;
type
TPersona = class(TObject, IPersona)
strict private
FNome: string;
function GetNome: string;
procedure SetNome(const Value: string);
protected
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
public
constructor Create(const ANome: string);
destructor Destroy; override;
end;
var
Form4: TForm4;
implementation
{$R *.dfm}
procedure TForm4.FormCreate(Sender: TObject);
begin
ReportMemoryLeaksOnShutdown := True;
end;
procedure TForm4.btn1Click(Sender: TObject);
var
p: IPersona;
begin
p := TPersona.Create('Fabio');
try
ShowMessage(p.Nome);
finally
FreeAndNil(p);
end;
end;
constructor TPersona.Create(const ANome: string);
begin
inherited Create;
FNome := ANome;
end;
destructor TPersona.Destroy;
begin
inherited Destroy;
end;
function TPersona._AddRef: Integer;
begin
Result := -1
end;
function TPersona._Release: Integer;
begin
Result := -1
end;
function TPersona.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if GetInterface(IID, Obj) then
Result := S_OK
else
Result := E_NOINTERFACE;
end;
function TPersona.GetNome: string;
begin
Result := FNome;
end;
procedure TPersona.SetNome(const Value: string);
begin
FNome := Value;
end;
end.
The access violation occurs because FreeAndNil receives an untyped var parameter that is expected to be an object reference. You are passing an interface reference which does not meet the requirement. Unfortunately you only find out at runtime. This is, in my view, the strongest point against the use of FreeAndNil.
Your reference counting disables lifetime management by the interface reference counting mechanism. In order to destroy an object you need to call its destructor. And in order to do that you must have access to the destructor. Your interface doesn't expose the destructor (and it should not). So, we can deduce that, in order to destroy the object, you need to have an object reference.
Here are some options:
var
obj: TPersona;
intf: IPersona;
....
obj := TPersona.Create('Fabio');
try
intf := obj;
//do stuff with intf
finally
obj.Free;
// or FreeAndNil(obj) if you prefer
end;
Or you can do it like this
var
intf: IPersona;
....
intf := TPersona.Create('Fabio');
try
//do stuff with intf
finally
(intf as TObject).Free;
end;
You cannot use FreeAndNil() with an interface reference, only an objct reference. Had you left the interface's reference count enabled, you would simply assign nil to the interface reference (or just let it go out of scope) to free the object correctly, eg:
type
TPersona = class(TInterfacedObject, IPersona)
strict private
FNome: string;
function GetNome: string;
procedure SetNome(const Value: string);
public
constructor Create(const ANome: string);
destructor Destroy; override;
end;
procedure TForm4.btn1Click(Sender: TObject);
var
p: IPersona;
begin
p := TPersona.Create('Fabio');
try
ShowMessage(p.Nome);
finally
p := nil;
end;
end;
But since you have disabled the reference count on the interface, you need to go back to using normal object reference variables in your code, eg:
procedure TForm4.btn1Click(Sender: TObject);
var
p: TPersona;
intf: IPersona;
begin
p := TPersona.Create('Fabio');
try
if Supports(p, IPersona, intf) then
ShowMessage(intf.Nome);
finally
FreeAndNil(p);
end;
end;

Resources