Generics without parameterless constructors - delphi

Can someone explain why in the code below, class1List does not require class1 to have a parameterless constructor, but class2list does require class 2 to have a parameterless constructor.
unit Unit11;
interface
uses
System.Generics.Collections;
type
class1 = class
public
constructor Create( const i : integer ); virtual;
end;
class1List<T : class1 > = class( TObjectList< T > )
public
function AddChild( const i : integer ) : T;
end;
class2 = class
public
constructor Create( const i : integer );
end;
class2List<T : class2 > = class( TObjectList< T > )
public
function AddChild( const i : integer ) : T;
end;
implementation
{ class1List<T> }
function class1List<T>.AddChild(const i: integer): T;
begin
Result := T.Create( i );
inherited Add( Result );
end;
{ class2List<T> }
function class2List<T>.AddChild(const i: integer): T;
begin
Result := T.Create( i );
inherited Add( Result );
end;
{ class1 }
constructor class1.Create(const i: integer);
begin
end;
{ class2 }
constructor class2.Create(const i: integer);
begin
end;
end.

function class1List<T>.AddChild(const i: integer): T;
begin
Result := T.Create( i );
inherited Add( Result );
end;
The constructor of class1 is declared virtual. Therefore the compiler knows that T.Create yields an instance of T whose intended constructor has been called. Hence the compiler accepts this code. Note that earlier versions of the compiler would reject this code and force you to use the following cast
Result := T(class1(T).Create( i ));
But more recent versions of the compiler have removed the need for such trickery.
function class2List<T>.AddChild(const i: integer): T;
begin
Result := T.Create( i );
inherited Add( Result );
end;
The constructor of class2 is not virtual and so the compiler knows that were it to call the constructor of class2, likely the class would not be properly initialised. It is prepared to call a parameterless constructor from the specialised type T if one exists, and you apply the constructor constraint when you declare the generic type. However, the language offers no way to apply a constructor constraint for constructors that accept parameters.
Now, you could apply the constructor constraint, but that would do no good. In order for the instance to be initialised properly, you need to call the constructor with the parameter. Which means, in practical terms, that you should use the first approach using a virtual constructor.
Don't be tempted to cast your way out of this hole. This code will compile
Result := T(class2(T).Create( i ));
but will likely not do what you want. This will call the static constructor of class2 which is surely not what you want.

Related

How can I mock a method call with var parameter in DUnitX and Spring4D 1.2.2

How can I mock an interface method call like procedure foo( var i_ : integer ). The tested method local variable passed as a var param, so the test must use Arg.IsAny (The test does not access it). The result value is not the same as the out value of the var param, because the tested method does some processing on it before gives back as a result. The commented When variations in the test does not compile. The current one compiles but results an undefined value (mock Executes does not call at all, because the var=pointer values don't match).
How could I mock a method call with a var parameter?
unit Unit1;
interface
uses
DUnitX.TestFramework
, Spring.Mocking
;
type
IMyInterface = interface ( IInvokable )
['{606BA1D8-EAEC-42CB-A774-911628FD2E6C}']
procedure foo( var x_ : integer );
end;
TMyClass = class
private
fMyInterface : IMyInterface;
public
constructor Create( myInterface_ : IMyInterface );
function bar : integer;
end;
[TestFixture]
TMyClassUnitTest = class
public
[Test]
procedure bar;
end;
implementation
constructor TMyClass.Create( myInterface_ : IMyInterface );
begin
inherited Create;
fMyInterface := myInterface_;
end;
function TMyClass.bar : integer;
var
i : integer;
begin
fMyInterface.foo( i );
result := i + 1;
end;
procedure TMyClassUnitTest.bar;
var
myInterfaceMock : Mock<IMyInterface>;
myClass : TMyClass;
i : integer;
procedure prepareMyInterfaceFooCall( fooVarValue_ : integer );
var
ii : integer;
begin
ii := 7;
myInterfaceMock.Setup.Executes(
function ( const args_ : TCallInfo ) : TValue
begin
args_[0] := TValue.From<integer>( fooVarValue_ );
end
//).When.foo( Arg.IsAny<integer> );
//).When.foo( integer( Arg.IsAny<integer> ) );
).When.foo( ii );
end;
begin
prepareMyInterfaceFooCall( 5 );
myClass := TMyClass.Create( myInterfaceMock );
try
i := myClass.bar;
finally
FreeAndNIL( myClass );
end;
Assert.AreEqual( 6, i );
end;
end.
1.2.2 cannot do this but 2.0 can do (currently develop branch)
Here is the relevant change to your code:
procedure prepareMyInterfaceFooCall(expectedValue: Integer);
begin
myInterfaceMock.Setup.Executes
// put the wildcard matcher because your code passes a non initialized variable
// a matcher on the When() always has priority over any individual parameter matching
.When(Args.Any)
// use the Arg.Ref syntax specifying the return value
.foo(Arg.Ref<Integer>(expectedValue).Return);
end;

How to test possible memory leaks caused by references after the destruction of the tested object (DUnitX, Spring4D 1.2.2)

TMyClass contains two references. A reference to an IList<integer> and a reference to IMyInterface. The mocking of IList<integer> is not necessary. The framework probably well tested, well predictable behavior, so I could see it as a Data object. But IMyInterface is an untested service, so I should mock it in a unit test.
I want to check for memory leaks so I want to test the modifications of the RefCount-s of the references after the subject destroyed.
The 'RefCount' of the IList<integer> changes in the right way. But I can't say the same for the mocked IMyInterface (in my solution). How could I test that the references does not cause memory leaks? Or a test like this is an integration test and should I test it always with real instances?
unit Unit1;
interface
uses
DUnitX.TestFramework
, Spring.Collections
, Spring.Mocking
;
type
IMyInterface = interface ( IInvokable )
['{76B13784-6CCF-4A87-882C-E624F003B082}']
procedure foo;
end;
TMyClass = class
private
fList : IList<integer>;
fMyInterface : IMyInterface;
public
constructor Create( list_ : IList<integer>; myInterface_ : IMyInterface );
end;
[TestFixture]
TMyClassTest = class
protected
function getInterfaceRefCount( const interface_ : IInterface ) : integer;
public
[Test]
procedure listRefCountTest;
[Test]
procedure myInterfaceRefCountTest;
end;
implementation
uses
System.SysUtils
;
constructor TMyClass.Create( list_ : IList<integer>; myInterface_ : IMyInterface );
begin
inherited Create;
fList := list_;
fMyInterface := myInterface_;
end;
function TMyClassTest.getInterfaceRefCount( const interface_ : IInterface ) : integer;
begin
result := ( interface_ as TInterfacedObject ).RefCount;
end;
procedure TMyClassTest.listRefCountTest;
var
list : IList<integer>;
myInterfaceMock : Mock<IMyInterface>;
myClass : TMyClass;
listRefCount : integer;
begin
list := TCollections.CreateList<integer>;
myClass := TMyClass.Create( list, myInterfaceMock );
try
listRefCount := getInterfaceRefCount( list );
finally
FreeAndNIL( myClass );
end;
Assert.AreEqual( listRefCount-1, getInterfaceRefCount( list ) );
end;
procedure TMyClassTest.myInterfaceRefCountTest;
var
list : IList<integer>;
myInterfaceMock : Mock<IMyInterface>;
myClass : TMyClass;
myInterfaceRefCount : integer;
begin
list := TCollections.CreateList<integer>;
myClass := TMyClass.Create( list, myInterfaceMock );
try
myInterfaceRefCount := getInterfaceRefCount( myInterfaceMock.Instance );
finally
FreeAndNIL( myClass );
end;
Assert.AreEqual( myInterfaceRefCount-1, getInterfaceRefCount( myInterfaceMock.Instance ) );
end;
initialization
TDUnitX.RegisterTestFixture(TMyClassTest);
end.
Memoryleak checking does not need to be done explicitly - I suggest using https://github.com/shadow-cs/delphi-leakcheck for that - it can seemlessly integrated with either DUnit or DUnitX and automatically provides you with all the information you need when a leak occurs (opposed to only telling you "there was a leak of x bytes" which out of the box DUnit does by simply comparing allocated bytes before and after running the test)

How to define a parameter of type generic list with constructor constraint?

I want to define three base classes, TMyBaseClass that keeps data, TMyBaseClassList that holds a list of instances of TMyBaseClass, and TMyBaseClassReader that scrolls through a dataset and fills a TMyBaseClassList object. This is my code:
TMyBaseClass = class
public
// properties
constructor Create;
end;
TMyBaseClassList<T: TMyBaseClass, constructor> = class(TObjectList<TMyBaseClass>)
public
function AddNew: T;
end;
TMyBaseClassReader<T: TMyBaseClass> = class
public
class procedure ReadProperties(const DataSet: TCustomADODataSet;
const Item: T); virtual; abstract;
class procedure ReadDataSet(const DataSet: TCustomADODataSet;
const List: TMyBaseClassList<T>);// <- E2513
end;
...
constructor TMyBaseClass.Create;
begin
inherited;
end;
function TMyBaseClassList<T>.AddNew: T;
begin
Result := T.Create;
Add(Result);
end;
class procedure TMyBaseClassReader<T>.ReadDataSet;
var
NewItem: T;
begin
while not DataSet.Eof do
begin
NewItem := List.AddNew;
ReadProperties(DataSet, NewItem);
DataSet.Next;
end;
end;
Then I want to derive child classes and only implement ReadProperties method. But I'm getting an E2513 error:
E2513 Type parameter 'T' must have one public parameterless constructor named Create
What is the problem and how can I fix it?
The error means that the compiler cannot be sure that T meets the requirements. Declare the derived class like so
TMyBaseClassReader<T: TMyBaseClass, constructor>

Delphi - Cannot cast TVirtualInterface to base interface of virtualized interface

Howdey,
I am using TVirtualInterface to implement some interfaces. Those interfaes represent Keys that can be found in a DB. I generate the interface definitions with a custom made code generator. For example :
// Base code
IKey = interface
function KeyFields : string;
function KeyValues : Variant;
function GetKeyValue(const aKeyName : string) : Variant;
procedure SetKeyValue(const aKeyName : string; Value : Variant);
end;
// Generated code
ITable1Key = interface(IKey)
end;
ITable1Key1 = interface(ITable1Key)
procedure SetField1(const Value : string);
function GetField1 : string;
property Field1 : string read GetField1 write SetField1;
end;
ITable1Key2 = interface(ITable1Key)
procedure SetField1(const Value : string);
function GetField1 : string;
property Field1 : string read GetField1 write SetField1;
procedure SetField2(const Value : string);
function GetField2 : string;
property Field2 : string read GetField1 write SetField1;
end;
// Other generated declarations
I use the TVirtualInterface to implement each IKey interface instead of implementing them one by one.
Though, in my TVirtualInterface :
TKey = TVirtualInterface
public
constructor Create(aType : PTypeInfo);
function Cast : IKey;
end;
TKey<T : IKey>
public
constructor Create; reintroduce;
function Cast : T;
end;
constructor TKey.Create(aType : PTypeInfo)
begin
inherited Create(aType, aHandlerMethod);
end;
function TKey.Cast;
var
pInfo: PTypeInfo;
begin
pInfo := TypeInfo(IKey);
if QueryInterface(GetTypeData(pInfo).Guid, Result) <> 0 then
begin
raise Exception.CreateFmt('Sorry, TKey is unable to cast %s to its interface ', [string(pInfo.Name)]);
end;
end;
constructor TKey<T>.Create;
begin
inherited Create(TypeInfo(T));
end;
function TKey<T>.Cast;
var
pInfo: PTypeInfo;
begin
pInfo := TypeInfo(T);
if QueryInterface(GetTypeData(pInfo).Guid, Result) <> 0 then
begin
raise Exception.CreateFmt('Sorry, TKey<T> is unable to cast %s to its interface ', [string(pInfo.Name)]);
end;
end;
I have no problem casting the TKey virtual interface to the T type using the TKey.Cast method, though TKey.Cast returns a Interface not supported error.
I checked in System.Rtti for the part that wasn't working the way I wanted it to :
function TVirtualInterface.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if iid = FIID then
begin
_AddRef;
Pointer(Obj) := #VTable;
Result := S_OK;
end
else
Result := inherited
end;
Now, how can I force the TVirtualInterface to cast itself to a IID that is a parent interface of the FIID field ? Do I have to create another instance of the TVirtualInterface for the IKey interface ?
Thank you very much.
You are misusing TVirtualInterface. It is just an RTTI helper, you should not be deriving from it at all. You should be deriving from TInterfacedObject instead.
Also, both of your TKey classes are ignoring the PTypeInfo that is passed to the constructor. The non-Generic TKey.Cast() is always querying for IKey only, never a descendant interface. And the Generic TKey<T>.Cast is always re-querying T's RTTI to get its IID. So get rid of the PTypeInfo in the constructor, it is wasted.
Since the non-Generic TKey is just a base class that doesn't actually implement any derived interfaces at all, TKey.QueryInterface() will always fail for any interface other than IKey itself. At least the Generic TKey can query a derived interface.
Your Cast functions are redundant anyway, since you can use the as operator, or the SysUtils.Supports() function, to cast one interface to another. These are the preferred methods, not using QueryInterface() manually.
In any case, your interfaces are missing IIDs in their declarations, so you can't cast between interfaces anyway.
Try something more like this:
// Base code
IKey = interface
['{D6D212E0-C173-468C-8267-962CFC3FECF5}']
function KeyFields : string;
function KeyValues : Variant;
function GetKeyValue(const aKeyName : string) : Variant;
procedure SetKeyValue(const aKeyName : string; Value : Variant);
end;
// Generated code
ITable1Key = interface(IKey)
['{B8E44C43-7248-442C-AE1B-6B9E426372C1}']
end;
ITable1Key1 = interface(ITable1Key)
['{0C86ECAA-A8E7-49EB-834F-77DE62BE1D28}']
procedure SetField1(const Value : string);
function GetField1 : string;
property Field1 : string read GetField1 write SetField1;
end;
ITable1Key2 = interface(ITable1Key)
['{82226DE9-221C-4268-B971-CD72617C19C7}']
procedure SetField1(const Value : string);
function GetField1 : string;
property Field1 : string read GetField1 write SetField1;
procedure SetField2(const Value : string);
function GetField2 : string;
property Field2 : string read GetField1 write SetField1;
end;
// Other generated declarations
type
TKey = class(TInterfacedObject, IKey)
public
function Cast : IKey;
// IKey methods...
end;
TKey<T : IKey> = class(TInterfacedObject, IKey, T)
public
function Cast : T;
end;
TTable1Key = class(TKey, IKey, ITable1Key)
end;
TTable1Key1 = class(TTable1Key, IKey, ITable1Key, ITable1Key1)
public
// ITable1Key1 methods...
end;
TTable1Key2 = class(TTable1Key, IKey, ITable1Key, ITable1Key2)
public
// Table1Key2 methods...
end;
// and so on ...
function TKey.Cast: IKey;
begin
if not Supports(Self, IKey, Result) then
raise Exception.Create('Sorry, unable to cast to IKey');
end;
function TKey<T>.Cast: T;
begin
if not Supports(Self, GetTypeData(TypeInfo(T)).Guid, Result) then
raise Exception.CreateFmt('Sorry, unable to cast to %s', [string(TypeInfo(T).Name)]);
end;
// other class methods as needed ...
Also note how the derived classes have to repeat the interfaces implemented by their base classes. That is a known Delphi limitation. Derived classes do not inherit base class interfaces. Each class has to explicitly specify the interfaces it implements, even if the actual implementation is in a base class.

How to do generic typecasting in delphi?

I have a base class test define below
type
Tinfo = procedure of object;
Test = class(TObject)
public
procedure Add ( const a : Tinfo ); reintroduce ;
end;
procedure Test.Add(const a: Tinfo);
begin
Writeln('base class add function');
// dosomething more
end;
and I have a derived generic class from this base class
TTesting<T> = class(Test)
public
procedure Add ( const a : T ); reintroduce ;
end;
and I am typecasting T to Tinfo but it gives me the error
procedure TTesting<T>.Add(const a : T );
begin
inherited Add(Tinfo(a) ); // gives me error here
end;
is there any way I can implement this?
First your cast is wrong, you obviously want to cast a and not T.
However if you want to type cast on a procedure of object which is a type that cannot be polymorphic in any way it makes no sense to put that into a generic type at all.
What should T be? It only can be a TInfo in your code.
If you however want T to be any event/method type you should store a TMethod in your base class and then work with that in your generic class. But remember that you cannot have a constraint that limits T to be an event type. So you might check that in your constructor.
type
PMethod = ^TMethod;
Test = class(TObject)
public
procedure Add(const a: TMethod ); reintroduce ;
end;
procedure Test.Add(const a: TMethod);
begin
Writeln('base class add function');
// dosomething more
end;
type
TTesting<T> = class(Test)
public
constructor Create;
procedure Add(const a: T); reintroduce ;
end;
constructor TTesting<T>.Create;
begin
Assert(PTypeInfo(TypeInfo(T)).Kind = tkMethod);
inherited Create;
end;
procedure TTesting<T>.Add(const a: T);
begin
inherited Add(PMethod(#a)^);
end;

Resources