Hi

Hi,
we have a very special problem regarding variants and TValue:

As result of an event we need to run functions from a class by rtti.

We get the instance, classname, methodname, and the parameters 
of the function. (function EventCallMethod).

The problem is, that we get the parameters of the function as a Variant.

The invoke function needs them  as array of TValue.
So we have to convert them from Variant to array of TValue.
For normal kinds (tkinteger, tkstring, ..) this is no problem.
But now we have an function with tkDynArray Parameter (TMyClass.GetValueFromDB)
Our implementation for this (VariantToTValue ) works, 
but we think, there must be a better way to create the TValue 
for tkDynArray as the implemented.

type
  // Function we want to call

  TMyClass = class
    function GetValueFromDB(SQLString: string; Params: TArray): variant;
  end;



function EventCallMethod(Instance: TObject; ClassType: TClass; const MethodName: string; var Params: Variant): Variant;
var
  ctx: TRttiContext;
  t  : TRttiType;
  m  : TRttiMethod;
  v  : TVallueArray;
  erg: TValue;

begin
  ctx := TRttiContext.Create;
  try
    t := ctx.GetType(ClassType);
    m := t.GetMethod(MethodName);
    if Assigned(m) then
    begin
      VariantToTValue(m, v, Params);  <<-- Here the Params (Variant) are converted to TVallueArray;
      erg := m.Invoke(Instance, v);    
      Result := TValueToResult(erg);  
    end
  [...] 

end;


procedure VariantToTValue(m: TRttiMethod; var v: TVallueArray; var Params: Variant);
var
  a: TArray;
  pa: rtti.TRttiParameter;
  i : integer;
begin
  a := m.GetParameters;
  if length(a) > 0 then
  begin
    i := length(a);
    SetLength(v, i);
    for i := 0 to length(a) - 1 do
    begin
      pa := a[i];
     // todo: replace if structure by case … of 
      if pa.ParamType.TypeKind = tkDynArray then
      begin
        if pa.ParamType.Name = 'TArray' then
          v[i] := TValue.From> (Params[i])
        else if pa.ParamType.Name = 'TArray' then
          v[i] := TValue.From> (Params[i])
        else if pa.ParamType.Name = 'TArray' then
          v[i] := TValue.From> (Params[i])
        else
          raise Exception.Create('Undefined Type of Array');
      end
      else if pa.ParamType.TypeKind = tkVariant then
      begin
        v[i] := TValue.FromVariant(Params[i]);
      end
      .
      .
      .

Comments

  1. function DynArrayVariantToValue(const v: Variant; typeInfo: Pointer): TValue;
    var
      p: Pointer;
    begin
      p := nil;
      DynArrayFromVariant(p, v, typeInfo);
      TValue.MakeWithoutCopy(@p, typeInfo, Result);
    end;

    And please: don't use strings to compare types. That is what TypeInfo is for.

    ReplyDelete
  2. Here is another version of the function that checks the VarType so you don't have to pass the array typeinfo:

    function DynArrayVariantToValue(const v: Variant): TValue;
    begin
      case VarType(v) and not varArray of
        varSmallint: Result := TValue.From>(v);
        varInteger: Result := TValue.From>(v);
        varSingle: Result := TValue.From>(v);
        varDouble: Result := TValue.From>(v);
        varCurrency: Result := TValue.From>(v);
        varDate: Result := TValue.From>(v);
        varOleStr: Result := TValue.From>(v);
        varDispatch: Result := TValue.From>(v);
        varError: Result := TValue.From>(v);
        varBoolean: Result := TValue.From>(v);
        varVariant: Result := TValue.From>(v);
        varUnknown: Result := TValue.From>(v);
        varShortInt: Result := TValue.From>(v);
        varByte: Result := TValue.From>(v);
        varWord: Result := TValue.From>(v);
        varLongWord: Result := TValue.From>(v);
        varInt64: Result := TValue.From>(v);
        varUInt64: Result := TValue.From>(v);
        varUString: Result := TValue.From>(v);
      else
        raise EVariantTypeCastError.CreateRes(@SInvalidVarCast);
      end;
    end;

    ReplyDelete

Post a Comment