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
.
.
.
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
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
v[i] := TValue.From
else if pa.ParamType.Name = 'TArray
v[i] := TValue.From
else if pa.ParamType.Name = 'TArray
v[i] := TValue.From
else
raise Exception.Create('Undefined Type of Array');
end
else if pa.ParamType.TypeKind = tkVariant then
begin
v[i] := TValue.FromVariant(Params[i]);
end
.
.
.
function DynArrayVariantToValue(const v: Variant; typeInfo: Pointer): TValue;
ReplyDeletevar
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.
Here is another version of the function that checks the VarType so you don't have to pass the array typeinfo:
ReplyDeletefunction 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;