Generics and Array of const - delphi

type
TGeneric<T> = class(TBase)
public
procedure Swap(Harry : T);
procedure NotifyAll(AParams : T);
end;
i have two question
Is there any way i can typecast a generic variable in any other type like variant etc.
how can i pass generic type in function that accept array of const like here
Delphi - How can I pass Generic parameter to function that accept Array of const parameter

Is there any way I can typecast a generic variable in any other type like Variant etc.
As stated in my answer to the question you link, the variant type that works well with generics is TValue. You can make a TValue instance from a generic type using the From method:
procedure TMyClass.Foo<T>(const Item: T);
var
Value: TValue;
begin
Value := TValue.From<T>(Item);
end;
How can I pass generic type in function that accept array of const?
This is the exact same question that was asked in the question you linked to. The answer is the same as stated there, namely that you cannot pass a generic argument as a variant open array parameter. Again, TValue to the rescue. Instead of using a variant open array parameter, use an open array of TValue.
The bottom line here is that the variant type that works well with generics, and that is supplied with the Delphi RTL, is TValue.

Copying my code from linked question:
procedure TEventMulticaster<T>.Notify(AUser: T);
var
ParametersAsTValueArray: array[1 .. 1] of TValue;
begin
ParametersAsTValueArray[1] := TValue.From<T>(AUser);
NotifyAll(TValueArrayToArrayOfConst(ParametersAsTValueArray));
end;

Related

How to pass generic array as open array parameter in Delphi?

I have an enumerated type and I need to pass an array of this type as parameter:
type
TTest = (a,b,c);
procedure DoTest(stest: TArray<TTest>);
When I compile
DoTest([a]);
I receiv the error below:
Error: E2010 Incompatible types: 'System.TArray' and 'Set'*
So, how can I call DoTest without creating a variable of type TArray<TTest>?
I don't have a Delphi compiler available right now, so I cannot verify this, but to me
procedure DoTest(stest: TArray<TTest>);
doesn't declare stest as an open array parameter, but a dynamic array parameter. You do want
procedure DoTest(const stest: array of TTest);
One way to do what you want is to change the parameter to an open array of TTest, i.e.
procedure DoTest(const stest: array of TTest);
But supposed you don't want to change the parameter, and really want it to be a TArray<TTest>, then you can simply use the array pseudo-constructor syntax to call it (in almost all versions of Delphi, except the very old ones). Say you have something like:
type
TTest = (a, b, c);
procedure DoTest(const stest: TArray<TTest>);
// simple demo implementation
var
I: Integer;
begin
for I := Low(stest) to High(stest) do
Write(Integer(stest[I]), ' ');
Writeln;
end;
Then it can be called, using the Create syntax without having to declare a variable or having to fill it manually. The compiler will do this for you:
begin
DoTest(TArray<TTest>.Create(a, c, b, a, c));
end.
The output is, as expected:
0 2 1 0 2
The compiler may confuse a with another declaration.
Qualify the type like this:
DoTest([Ttest.a]);
Note:
This feature of initializing dynamic arrays was introduced in XE7.
I'm assuming that with "how can I call DoTest without creating a variable of type TArray" you want to avoid declaring and initializing local variable, ie code like
var arr: TArray<TTest>;
begin
SetLength(arr, 1);
arr[0] := a;
DoTest(arr);
For this you can use the array constructor like:
DoTest(TArray<TTest>.Create(a));
This syntaxs is supported at least since Delphi 2010.

Can a property setter use open array parameter?

The doc says dynamic arrays can be passed into a function/procedure using open array parameters.
For example, the following works, in accordance with the doc.
type
TAInteger = array of Integer;
function Work(const A: array of Integer): Integer;
begin
Result := Length(A);
end;
However, the code below complains about E2008 Incompatible types:
type
TTest = class
private
procedure SetIntegerArray(const Value: array of Integer);
published
property Value: TAInteger write SetIntegerArray;
end;
I am confused why there is the "Incompatible types" error, and whether a property setter can use open array parameters ?
Can a property setter use open array parameter?
No.
The type of the parameter that contains the new property value must be compatible with the property's type. An open array is not a type. Your property setter must be written like this:
procedure SetIntegerArray(const Value: TAInteger);

Delphi - How can I pass Generic parameter to function that accept Array of const parameter

I have a 'Base class' which contain a 'function' that accept parameter of type 'Array of const' as shown below:-
type
TBaseClass = class(TObject)
public
procedure NotifyAll(const AParams: array of const);
end;
procedure TBaseClass.NotifyAll(const AParams: array of const);
begin
// do something
end;
I have another 'Generic class' that is derived from the 'base class' ( defined above )
type
TEventMulticaster<T> = class(TBaseClass)
public
procedure Notify(AUser: T); reintroduce;
end;
procedure TEventMulticaster<T>.Notify(AUser: T);
begin
inherited NotifyAll([AUser]); ERROR HERE
end;
Every time I compile this code it gives error saying:
Bad argument type in variable type array constructor
What does it referring to be wrong?
You cannot pass a Generic argument as a variant open array parameter. The language Generics support simply does not cater for that.
What you can do instead is wrap the Generic argument in a variant type, for instance TValue. Now, you cannot pass TValue instances in a variant open array parameter either, but you can change NotifyAll() to accept an open array of TValue instead:
procedure NotifyAll(const AParams: array of TValue);
Once you have this in place, you can call it from your Generic method like so:
NotifyAll([TValue.From<T>(AUser)]);
Fundamentally, what you are attempting to do here is combine compile-time parameter variance (Generics) with run-time parameter variance. For the latter, there are various options. Variant open array parameters are one such option, but they do not play well with Generics. The alternative that I suggest here, TValue, does have good interop with Generics.
The System.Rtti unit has something exactly for you needs, but not widely known:
TValueArrayToArrayOfConst() and
ArrayOfConstToTValueArray()
So your implementation should be:
procedure TEventMulticaster<T>.Notify(AUser: T);
var
ParametersAsTValueArray: array[1 .. 1] of TValue;
begin
ParametersAsTValueArray[1] := TValue.From<T>(AUser);
NotifyAll(TValueArrayToArrayOfConst(ParametersAsTValueArray));
end;
Notes:
TValueArrayToArrayOfConst()'s result is a non-owning container. It contains memory pointers backed by the source array of the TValue container. ParametersAsTValueArray is alive and not being altered while the array of const is used.
One of these 2 Rtti procedures has a bug with regard to TClass values processing. TClass becomes a Pointer on some stage, and string.Format() breaks because Pointer and TClass are not the same thing. Perform tests on all TVarRec.VType, they are not so many, much less that Variant's VType.

type inference based on generic argument type (Delphi)

I'm trying to write a generic function that accepts matching parameter types.
Delphi does infer the type parameter correctly in the simple case of plain arguments.
eg:
type
TFoo = class
function Pair<T>(e1, e2: T): TList<T>;
end;
calling this with aFoo.Pair(1, 2); works perfectly fine, but when I change the parameter signature to a generic type
type
TFoo = class
function InsertInto<T>(aList: TList<T>; aVal: T): TList<T>;
end;
and try to call it
aFoo.InsertInto(TList<String>.Create, 'bar');
then the compiler complains about it:
E2010 Incompatible types: 'Generics.Collections.TList<uTest.TFoo.InsertInto.T>' and 'Generics.Collections.TList<System.String>'
Is there any way I can write this (or a similar) method, so that the client doesnt have to specity the type parameter?
aFoo.InsertInto<String>(TList<String>.Create, 'bar');
My guess is that comes from the strongly typed nature of Delphi.
uTest.TFoo.InsertInto.T is equivalent to System.String but it's actually a different type.
Much like in this example where Int1 and Int2 are not of the same type:
var
Int1: array[1..10] of Integer;
Int2: array[1..10] of Integer;
...
Int1 := Int2; // <== BOOM! E2008 Incompatible types (in XE2)
The actual problem is not with type inference but with the types not being compatible per the strict rules of Pascal/Delphi.

Untyped / typeless parameters in Delphi

What type are parameters without type like in the class TStringStream:
function Read(var Buffer; Count: Longint): Longint; override;
What is the type of Buffer parameter (is it a type of Pointer ?).
I wrote an article about this very topic a few years ago:
What is an untyped parameter?
Untyped parameters are used in a few situations; the TStream.Read method you ask about most closely matches with the Move procedure I wrote about. Here's an excerpt:
procedure Move(const Source; var Dest; Count: Integer);
The Move procedure copies data from an arbitrary variable
into any other variable. It needs to accept sources and destinations of
all types, which means it cannot require any single type. The procedure
does not modify the value of the variable passed for Source, so that
parameter’s declaration uses const instead of var, which is the
more common modifier for untyped parameters.
In the case of TStream.Read, the source is the stream's contents, so you don't pass that in as a parameter, but the destination is the Buffer parameter shown in the question. You can pass any variable type you want for that parameter, but that means you need to be careful. It's your job, not the compiler's, to ensure that the contents of the stream really are a valid value for the type of parameter you provide.
Read the rest of my article for more situations where Delphi uses untyped parameters.
Check out the Delphi help for "Untyped parameters"
You can pass in any type, but you have to cast it in the implementation. Help says that you cannot pass it a numeral or untyped numeric constant. So basically you have to be know what type to expect, and compiler can not help you, so you need a good reason to do it this way. I suppose it could be of use if you need the method to handle incompatible types, but then again you could write several overloaded versions for each expected type, I would suggest that as a better solution.
Perhaps surprisingly, it is legal to pass a dereferenced pointer as an untyped parameter. And the pointer itself doesn't even have to have a type.
procedure SomeMethod(var aParameter);
∶
procedure CallSomeMethod(aIsInteger : Boolean);
type
buffer : Pointer;
intValue : Integer;
realValue : Single;
begin
if aIsInteger then
begin
buffer := #intValue;
end
else
begin
buffer := #realValue;
end;
SomeMethod(buffer^);
Of course it would probably have been easier if the parameter to SomeMethod() had been a pointer, but this might not be under your control.
var in a parameter list is the Delphi syntax for call by reference. It can be typed as e.g. the AllowChange parameter in the OnChanging handler of an Listview:
procedure TSomeForm.LVOnChanging(Sender: TObject; ...; var AllowChange: Boolean);
begin
if SomeProblemOccurred then
AllowChange := False;
end;
or untyped as in your example.

Resources