I am currently stuck with a compiling error, no one in our company can help and I am sadly not finding the correct search patterns for SO or google.
As code I am using 2 Interfaces, inherited and 2 Classes, inherited.
The following code reproduces the error:
program Project22;
{$APPTYPE CONSOLE}
type
IStorageObject = interface(IInterface)
end;
TObjectStorage<T: IStorageObject> = class(TObject)
end;
IKeyStorageObject<TKey> = interface(IStorageObject)
end;
TKeyObjectStorage<TKey, T: IKeyStorageObject<TKey>> = class(TObjectStorage<T>)
end;
TImplementingClass<TKey> = class(TInterfacedObject, IKeyStorageObject<TKey>)
end;
begin
TKeyObjectStorage<Integer, TImplementingClass<Integer>>.Create;
end.
The compiler error for 'TKeyObjectStorage' is:
[DCC Error] Project22.dpr(11): E2514 Type parameter 'T' must support interface 'IStorageObject'
What I think is, that the compiler is not recognizing that Parameter T of the Class 'TKeyObjectStorage' correctly.
It should be correct, since the wanted Type 'IKeyStorageObject' has the parent type IStorageObject.
Why is this not working? What am I doing wrong? Is this not possible in Delphi?
Update
The original question had a problem which I identified (see below). However, the fix I describe there is fine for XE3 and later, but that program below does not compile in XE2. Thus I conclude that this is an XE2 generics compiler bug.
Anyway, here's a workaround for Delphi XE2:
{$APPTYPE CONSOLE}
type
IStorageObject = interface(IInterface)
end;
TObjectStorage<T: IStorageObject> = class(TObject)
end;
IKeyStorageObject<TKey> = interface(IStorageObject)
end;
TKeyObjectStorage<TKey; T: IKeyStorageObject<TKey>, IStorageObject> = class(TObjectStorage<T>)
end;
TImplementingClass<TKey> = class(TInterfacedObject, IStorageObject, IKeyStorageObject<TKey>)
end;
begin
TKeyObjectStorage<Integer, TImplementingClass<Integer>>.Create;
end.
Original answer
It would have been better if you had provided a complete program that exhibited the compiler error. You need to attempt to instantiate an object to see that error.
But, I think I've reproduced your problem. So I believe that the issue is that this code:
TKeyObjectStorage<TKey, T: IKeyStorageObject<TKey>> = ...
applies the generic constraint to both TKey
and T
. Now, clearly you only want the constraint to apply to T
so you'll need to write:
TKeyObjectStorage<TKey; T: IKeyStorageObject<TKey>> = ...
Here's a short program that compiles following the change in Delphi XE3:
{$APPTYPE CONSOLE}
type
IStorageObject = interface(IInterface)
end;
TObjectStorage<T: IStorageObject> = class(TObject)
end;
IKeyStorageObject<TKey> = interface(IStorageObject)
end;
TKeyObjectStorage<TKey; T: IKeyStorageObject<TKey>> = class(TObjectStorage<T>)
end;
TImplementingClass<TKey> = class(TInterfacedObject, IKeyStorageObject<TKey>)
end;
begin
TKeyObjectStorage<Integer, TImplementingClass<Integer>>.Create;
end.
This is quite a nuance, the changing of a comma to a semi-colon. Programming by significant punctuation is never much fun. That said, you are familiar with the difference between commas and semi-colons in formal parameter lists and so it should not come as too much of a surprise to see the same distinction drawn here.
The documentation does cover this mind you:
Multiple Type Parameters
When you specify constraints, you separate multiple type parameters by
semicolons, as you do with a parameter list declaration:
type
TFoo<T: ISerializable; V: IComparable>
Like parameter declarations, multiple type parameters can be grouped
together in a comma list to bind to the same constraints:
type
TFoo<S, U: ISerializable> ...
In the example above, S
and U
are both bound to the ISerializable
constraint.