Delphi - Interface inheritance with generics - delphi

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.

Related

Delphi - How do you resolve a conflict when a unit name is the same as a property name?

The trivial example below is a condensation of a problem I had trying to resolve a conflict where I had an enumerated type member with the same name as a VCL member.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
type
TSomeType = (
alNone,
alSome,
alMany) ;
procedure TForm1.FormCreate(Sender: TObject);
begin
Self.Align := alNone ; // 1. type mismatch
Self.Align := Controls.alNone ; // 2. "Controls" is also a property of TForm
end ;
end.
The first assignment fails because the compiler thinks alNone is the one I declared and not the TAlign member defined in Controls.pas.
The second one fails because it took Controls to mean the TForm property of that name.
I realise there are ways around this (renaming the alNone member being the simplest), but I'm curious as to whether there is a way of qualifying a reference to a property in another unit where the unit name conflicts with an identifier in the current scope.
Qualify it with the type name:
TAlign.alNone
I had not realised when I wrote this that the compiler version was relevant. This syntax only became available in Delphi 2010 or XE. There the answer is not appropriate for the tagged version, Delphi 2007. Deltics answer covers much more detail.
As David's answer suggests, for an enum type or other situation where a type can be used to qualify the identifier involved then you can of course simply use the type name as required:
someAlign := TAlign.alNone;
someMyType := TMyType.alNone;
This use of enums is referred to as "scoped enums" and is not supported in older versions of the Delphi compiler. I believe XE2 may have been when it was introduced. Certainly this was the version that made scoping enums in this way mandatory by default.
Though it can be turned off via a compiler directive. When turned off, you can still use scoped enums but you are not required to.
In versions that support this, you must qualify any enums that are defined while this is turned on. You can choose whether to quality or not when using enums that are defined when this is turned off.
type
{$SCOPEDENUMS ON}
TFoo = (Black, White); // MUST qualify: eg. "TFoo.Black"
{$SCOPEDENUMS OFF}
TBar = (Black, White); // MAY qualify or not if/as needed
For older versions of Delphi without scoped enum support, or in situations where the identifier is not an enum member and cannot otherwise be qualified by type - for example if your identifiers conflict with some unit-level identifier (e.g. such as mrOk, in Controls) then you need to a little bit more work, but not much.
In these cases, simply define a new constant to create an unambiguous "local alias" for the constant in the other unit, and introduce this where the unit name is unambiguous. Similar to:
type
TMyResult = (
mrOk,
mrFailed) ;
const
Controls_mrOk = Controls.mrOk; // mrOk is a const, not an enum member

Delphi mocks with DSharp won't compile with enumerator values

I'm using DSharp mocks in Delphi XE5 (thanks Stefan!!) but have a problem with some enumerators. If I try to define an enumerator with specific values, the {$M+} directive causes the following error:
E2134 Type 'TMyEnum' has no type info
Here is a sample console app which sums it all up:
program DSharpMockEnum;
program DSharpMockEnum;
{$APPTYPE CONSOLE}
{$R *.res}
uses
DSharp.Testing.Mock,
System.SysUtils;
type
TBadEnum = (badEnum1 = 1, badEnum2); // <---- WONT COMPILE
TGoodEnum = (goodEnum1, goodEnum2); // This compiles OK
{$M+}
IBadMock = interface
['{34B3904E-3EBA-4C6E-BBA8-A40A67A32E7F}']
function GetEnum: TBadEnum;
procedure SetEnum(Value: TBadEnum);
property MyEnum: TBadEnum read GetEnum write SetEnum;
end;
{$M+}
IGoodMock = interface
['{34B3904E-3EBA-4C6E-BBA8-A40A67A32E7F}']
function GetEnum: TGoodEnum;
procedure SetEnum(Value: TGoodEnum);
property MyEnum: TGoodEnum read GetEnum write SetEnum;
end;
var
LGoodMock: Mock<IGoodMock>;
LBadMock: Mock<IBadMock>;
begin
try
Writeln('Good Mock');
LGoodMock.Setup.WillReturn(goodEnum1).Any.WhenCalling.MyEnum;
Writeln('Bad Mock');
LBadMock.Setup.WillReturn(badEnum1).Any.WhenCalling.MyEnum;
Writeln('Stop');
Readln;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Any ideas or suggestions? I prefer to avoid resetting all my enums since some are stored in the database and need specific values.
Delphi XE5.
Thanks.
Rick.
answer in error log. Enums with assigned values does not have RTTI. If u want enums with rtti and concrete values, u must use dummy emeration entries, like
TBadEnum = (badDummy0, badEnum1, badEnum2,badDummy3, badEnum4);
Here badDummy0 effectively stores 0, and badDummy3 is 3
Other option is to use some integer type

E2010 Incompatible Types, why?

I'm getting this error:
[DCC Error] JwaStrSafe.pas(2277): E2010 Incompatible types: 'PSTRSAFE_LPWSTR' and 'PSTRSAFE_LPTSTR'
The following is the relevant portion of code from JwaStrSafe.pas (from Jedi Api), I'm compiling with the symbol UNICODE defined:
type
STRSAFE_LPWSTR = PWIDECHAR;
PSTRSAFE_LPWSTR = ^STRSAFE_LPWSTR;
{$IFDEF UNICODE}
STRSAFE_LPTSTR = STRSAFE_LPWSTR;
PSTRSAFE_LPTSTR = ^STRSAFE_LPTSTR;
{$ELSE}
...
{$ENDIF}
...
//function declaration
function StringCchCopyExW(
{__out_ecount(cchDest)}pszDest : STRSAFE_LPWSTR;
{__in}cchDest : size_t;
{__in}const pszSrc : STRSAFE_LPCWSTR;
{__deref_opt_out_ecount(pcchRemaining^)}ppszDestEnd : PSTRSAFE_LPWSTR;
{__out_opt}pcchRemaining : PSize_t;
{__in}dwFlags : Cardinal) : HRESULT; stdcall; forward; external;
...
//var passed to function
ppszDestEnd : PSTRSAFE_LPTSTR;
...
{$IFDEF UNICODE}
result := StringCchCopyExW(pszDest, cchDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
{$ELSE}
result := StringCchCopyExA(pszDest, cchDest, pszSrc, ppszDestEnd, pcchRemaining, dwFlags);
{$ENDIF}
I get the error on the call of StringCchCopyExW, on parameter ppszDestEnd.
Looking at the type definition I understand that PSTRSAFE_LPTSTR is a pointer type to STRSAFE_LPTSTR which is just an alias of STRSAFE_LPWSTR, why are PSTRSAFE_LPTSTR and PSTRSAFE_LPWSTR incompatible?
Solution
Thanks to David's explanation I replaced
PSTRSAFE_LPTSTR = ^STRSAFE_LPTSTR;
with
PSTRSAFE_LPTSTR = PSTRSAFE_LPWSTR;
now the code compiles without errors.
Thanks
I can reproduce this easily enough in XE2, and I imagine it will behave the same in all other versions. To make it simpler I've cut it down to this:
program PointerTypeCompatibility;
{$APPTYPE CONSOLE}
type
A = Integer;
B = Integer;
var
ptA: ^A;
ptB: ^B;
begin
ptA := ptB;
end.
This also produces E2010. However, if you enable the type-checked pointers option, then the code compiles successfully. In fact the documentation of that compiler options states:
In the {$T-} state, distinct pointer types other than Pointer are incompatible (even if they are pointers to the same type). In the {$T+} state, pointers to the same type are compatible.
Thanks to Ken White for pointing me at the useful help topic Type Compatibility and Identity. The pertinent extracts are that types T1 and T2 are assignment compatible if:
T1 and T2 are compatible pointer types.
The documentation also states that types are type compatibile if:
Both types are (typed) pointers to the same type and the {$T+} compiler directive is in effect.
So this documents the observed behaviour and leads me to this example:
program PointerTypeCompatibilityTake2;
{$APPTYPE CONSOLE}
{$TYPEDADDRESS OFF}
var
P1,P2: ^Integer;
P3: ^Integer;
begin
P1 := P2;//compiles
P1 := P3;//E2008 Incompatible types
end.
So, to summarise:
When type-checked pointers is disabled, pointers are assignment compatible if the pointers are of the same type.
When type-checked pointers is enabled, pointers are assignment compatible if the pointers point to the the same type.
I have to confess to being ignorant of the history and reasoning behind the type-checked pointer setting, so I can't offer any explanation for why the compiler is the way it is.

Delphi RTTI unable to find interface

I'm trying to fetch an interface using D2010 RTTI.
program rtti_sb_1;
{$APPTYPE CONSOLE}
{$M+}
uses
SysUtils,
Rtti,
mynamespace in 'mynamespace.pas';
var
ctx: TRttiContext;
RType: TRttiType;
MyClass: TMyIntfClass;
begin
ctx := TRttiContext.Create;
MyClass := TMyIntfClass.Create;
// This prints a list of all known types, including some interfaces.
// Unfortunately, IMyPrettyLittleInterface doesn't seem to be one of them.
for RType in ctx.GetTypes do
WriteLn(RType.Name);
// Finding the class implementing the interface is easy.
RType := ctx.FindType('mynamespace.TMyIntfClass');
// Finding the interface itself is not.
RType := ctx.FindType('mynamespace.IMyPrettyLittleInterface');
MyClass.Free;
ReadLn;
end.
Both IMyPrettyLittleInterface and TMyIntfClass = class(TInterfacedObject, IMyPrettyLittleInterface) are declared in mynamespace.pas, in particular
unit mynamespace;
interface
type
IMyPrettyLittleInterface = interface
['{6F89487E-5BB7-42FC-A760-38DA2329E0C5}']
end;
TMyIntfClass = class(TInterfacedObject, IMyPrettyLittleInterface)
end;
//...
Do anyone know why this doesn't work? Is there a way to solve my problem? Thanks in advance!
This is a strange behavior you have found. You can find the type using:
RType := ctx.GetType(TypeInfo(IMyPrettyLittleInterface));
But after you have done this once you can access it by name, so if you need to access it by Name you can do the following to make it work.
Example Program:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils,
Rtti,
TypInfo,
Unit1 in 'Unit1.pas';
var
ctx : TRttiContext;
IType : TRttiType;
begin;
ctx := TRttiContext.Create;
IType := ctx.FindType('Unit1.IExample');
if Assigned(IType) then
begin
writeln('Found');
Writeln(IType.QualifiedName);
end
else
writeln('Not Found');
ReadLn;
end.
Example Unit:
unit Unit1;
interface
type
IExample = interface
['{D61F3245-13FB-44BF-A89D-BB358FAE7D19}']
end;
implementation
uses Rtti;
var
C : TRttiContext;
initialization
C.GetType(TypeInfo(IExample));
end.
The problem is that neither the VMT nor the typeinfo of classes which implement an interface contain any references to the typeinfo of those interfaces. The typeinfo for the interfaces is then eliminated by the linker if not otherwise referenced in the program. To fix this, there would need to be a typeinfo format change for classes to reference the implemented interfaces' typeinfo, or else all interfaces would need to be strongly linked into the executable. Other kinds of fixes, such as strong-linking interfaces that are implemented by linked-in classes without actually including references in the class typeinfo, are problematic owing to how the compiler's integrated smart linker works.
Another way around this issue is to use the directive {$STRONGLINKTYPES ON}. This will cause all types in the EXE, BPL or DLL's root type table (the index that lets the RTL map qualified names to types) to be linked in with strong fixups rather than weak fixups. Symbols that only have weak fixups referencing them are eliminated by the smart linker; if one or more strong fixups references the symbol, then it gets included in the final binary and the weak references don't get nil'ed (actually #PointerToNil) out.
As described in other answers, TypeInfo(ITheInterface) in non-dead code fixes the problem; this is because the TypeInfo() magic function creates a strong fixup to the interface.
Does IMyPrettyLittleInterface have a GUID? I don't think the RTTI system will recognize it if it doesn't.
RTTI is generated for types declared while the $M switch is active. Like all compiler directives, that switch has per-unit scope. You set it in your DPR file, but that setting has no effect in the unit where you declared your types.
Set that switch prior to your interface and class declarations:
type
{$M+}
IMyPrettyLittleInterface = interface
['{6F89487E-5BB7-42FC-A760-38DA2329E0C5}']
end;
TMyIntfClass = class(TInterfacedObject, IMyPrettyLittleInterface)
end;

What is the difference with these two sets of code [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 9 years ago.
What is the difference between these two pieces of code
type
IInterface1 = interface
procedure Proc1;
end;
IInterface2 = interface
procedure Proc2;
end;
TMyClass = class(TInterfacedObject, IInterface1, IInterface2)
protected
procedure Proc1;
procedure Proc2;
end;
And the following :
type
IInterface1 = interface
procedure Proc1;
end;
IInterface2 = interface(Interface1)
procedure Proc2;
end;
TMyClass = class(TInterfacedObject, IInterface2)
protected
procedure Proc1;
procedure Proc2;
end;
If they are one and the same, are there any advantages, or readability issues with either.
I guess the second means you cannot write a class that implements IInterface2 without implementing IInterface1, whilst with the first you can.
The two snippets of code have very different effects, and are in almost no way equivalent, if we are talking about Delphi for Win32 (Delphi for .NET has different rules).
A class that implements its interface must implement all the members of that interface's ancestors, but it does not implicitly implement the ancestors. Thus, attempts to assign instances of type TMyClass to locations of type IInterface1 will fail for the second case.
Related to the previous point, if IInterface1 and IInterface2 both had GUIDs, dynamic casts (using Supports or 'as') of interface references with a target type of IInterface1 would fail on instances of TMyClass in the second case.
The interface IInterface2 has an extra method in the second case, which it does not in the first.
Values of type IInterface2 in the second case are assignable to locations of type IInterface1; this is not true for the first case.
See for yourself in this example:
type
A_I1 = interface
end;
A_I2 = interface(A_I1)
end;
A_Class = class(TInterfacedObject, A_I2)
end;
procedure TestA;
var
a: A_Class;
x: A_I1;
begin
a := A_Class.Create;
x := a; // fails!
end;
type
B_I1 = interface
end;
B_I2 = interface
end;
B_Class = class(TInterfacedObject, B_I1, B_I2)
end;
procedure TestB;
var
a: B_Class;
x: B_I1;
begin
a := B_Class.Create;
x := a; // succeeds!
end;
begin
TestA;
TestB;
end.
First off, I'm assuming that the second example's declaration for IInterface2 is a typo and should be
IInterface2 = interface(Interface1)
because inheriting from itself is nonsensical (even if the compiler accepted it).
And "inheriting" is the key word there for answering your question. In example 1 the two interfaces are completely independent and you can implement one, the other, or both without problems. In example 2, you are correct that you can't implement interface2 without also implementing interface1, but the reason why that's so is because it makes interface1 a part of interface2.
The difference, then, is primarily structural and organizational, not just readability.
Assuming you meant
...
IInterface2 = interface(Interface1)
...
I interpret it the same as you, the second form requires a class implementing Interface2 to implement Interface1 as well, while the first form does not.
I guess the second means you cannot write a class that implements IInterface2 without implementing IInterface1, whilst with the first you can.
That would be the technical difference.
Which one is better depends very much on what the interfaces actually are. Does it ever make sense for an IInterface2 to exist without it also being an IInterface1?
If IInterface1 is "displayable" and IInterface2 is "storable," then the first option probably makes more sense. If IInterface1 is "vehicle" and IInterface2 is "truck," then the second option probably makes much more sense.

Resources