Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 519
akash_27 Avatar asked Oct 27 '14 09:10

akash_27


2 Answers

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.

like image 89
David Heffernan Avatar answered Nov 03 '22 01:11

David Heffernan


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:

  1. 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.
  2. 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.
like image 1
OCTAGRAM Avatar answered Nov 03 '22 02:11

OCTAGRAM