I'm writing some utility code for an old Delphi XE codebase; in order to keep things simpler and safer I've created a method meant to wrap a type-specific TProc<TReq>
(where TReq
is a generic class type) passed via parameters in a more broad TProc<TObject>
that should be fed to a third-party component later on, along with TClass(TReq)
and an additional string argument:
type
TREvoHostConnectionOptions = record
// [...]
procedure OnPush<TReq:class>(const pushMethod: string; const action: TProc<TReq>);
end;
// [...]
procedure TREvoHostConnectionOptions.OnPush<TReq>(const pushMethod: string; const action: TProc<TReq>);
var
rec: TRPushHandler;
begin
rec.PushMethod := pushMethod;
rec.PushModel := TClass(TReq);
rec.Handler :=
procedure(reqRawModel: TObject)
var
reqModel: TReq;
begin
// Conversione modello richiesta
reqModel := reqRawModel as TReq;
if Assigned(reqRawModel) and not Assigned(reqModel) then
raise EEvoHostException.Create(Format('Impossibile convertire il modello di tipo %s in %s.', [reqRawModel.ClassName, TClass(TReq).ClassName]));
// Azione
if Assigned(action) then
action(reqModel);
end;
PushHandlers.Add(rec);
end;
The previous method compiles successfully and if invoked like this, works as intended (although, having TObject
as the generic type defeats the purpose of the method):
opts.OnPush<TObject>('Test', procedure (reqModel: TObject) begin (* ... *) end);
However, if in the testing form unit I invoke it with a specifically crafted model class:
type
TTestModel = class(TObject)
strict private
_a, _b: string;
public
property A: string read _a write _a;
property B: string read _b write _b;
end;
I get the following compiler error in a completely irrelevant line (and completely different and totally unrelated method) in the calling unit:
[DCC Error] WndMain.pas(96): E2010 Incompatible types: 'TTestModel' and 'TObject'
* The displacement only occurs with this specific error, if I introduce an artificial syntax error anywhere else in the same file it gets reported at the correct line.
Any thoughts? Is this a compiler bug, and if so is there any way to get around it? Unfortunately I can't remove the :class
constraint on the method because otherwise the TClass(TReq)
conversion that happens inside the method raises (logically) another compilation error about TReq
not being a constrained class or interface type.
After further investigation, the problem seems to be caused by the as
conversion in the method, even though it was reported in the wrong file.
Changing the method like this seems to have solved it:
procedure TREvoHostConnectionOptions.OnPush<TReq>(const pushMethod: string; const action: TProc<TReq>);
var
rec: TRPushHandler;
begin
rec.PushMethod := pushMethod;
rec.PushModel := TClass(TReq);
rec.Handler :=
procedure(reqRawModel: TObject)
var
reqModel: TReq;
begin
// Conversione modello richiesta
if Assigned(reqRawModel) and not reqRawModel.ClassType.InheritsFrom(rec.PushModel) then
raise EEvoHostException.CreateFmt('Impossibile convertire il modello di tipo %s in %s.', [reqRawModel.ClassName, rec.PushModel.ClassName]);
// Azione
if Assigned(action) then
action(TReq(reqModel));
end;
PushHandlers.Add(rec);
end;