Reputation: 701
I have an OleVariant
or a Variant
value that, for example, was read with IXMLNode.GetAttributeNS
, making it a "String" (varOleStr
or varString
), and I would like to write that value with, for example, TRTTIField.SetValue
, requiring a TValue
assignment-compatible to TRTTIField.FieldType: TRTTIType
.
For the base types (along TVarType
and TRTTIType.TypeKind: TTypeKind
), instead of making each a single case: case VarType(Value) and varTypeMask of varXXXX: ... end
, I am looking for a general way to convert from OleVariant
or Variant
to a TValue
that then is assignment-compatible to a specific TRTTIType
.
What is the way to transition values between the Variant and the RTTI world?
Also, the Spring4D library is part of the project, in case that helps.
Update:
Technically I am looking for Convert
in the following code (converting in the Variant world):
var
Left: TRTTIField;
Right: OleVariant;
Temp: TValue;
Instance: Pointer;
begin
{ Examples: varOleStr --> varXXXX --> assignment-compatible TValue }
Right := 'False'; // varOleStr, as read with IXMLNode.GetAttributeNS
Right := Convert(Right, Left.FieldType); // making it possibly varBoolean
Temp := TValue.FromVariant(Right); // tkEnumeration, like Left.FieldType.TypeKind
Right := '2'; // varOleStr, as read with IXMLNode.GetAttributeNS
Right := Convert(Right, Left.FieldType); // making it possibly varInteger
Temp := TValue.FromVariant(Right); // tkInteger, like Left.FieldType.TypeKind
Right := '3.1415'; // varOleStr, as read with IXMLNode.GetAttributeNS
Right := Convert(Right, Left.FieldType); // making it possibly varDoiuble
Temp := TValue.FromVariant(Right); // tkFloat, like Left.FieldType.TypeKind
Right := 'Hello!'; // varOleStr, as read with IXMLNode.GetAttributeNS
Right := Convert(Right, Left.FieldType); // making it possibly varOleStr
Temp := TValue.FromVariant(Right); // tkUString, like Left.FieldType.TypeKind
{ ... and an assignment: }
Left.SetValue(Instance, Temp);
end;
I have found VariantChangeTypeEx
, however, I do not know how to relate Left.FieldType
to it to make the subsequent code work. -- I also would not mind to convert in the RTTI world and instead start out with Temp := TValue.FromVariant(Right)
(tkUString
) and then reach assignment compatibility somehow; so Temp.Kind
would become tkEnumeration
/Boolean, tkFloat
,... as given by Left.FieldType.TypeKind
.
How to assign a Variant with RTTI? Or, how to convert a Variant to a TValue to then assign it?
Note: RTTIField.SetValue
will fail with an EInvalidCast
if field type and value type differ in nature, as the RTTI will not attempt to change the value's nature. My difficulty here is to reach assignment compatibility.
Update: Given the answer, the following code sketches my solution:
procedure (const Value: Pointer; const RTTIField: TRTTIField; const XMLNode: IXMLNode);
var
Temp1: OLEVariant;
Temp2: TValue;
begin
Assert(XMLNode.HasAttribute(Ref, Namespace));
Temp1 := XMLNode.GetAttributeNS(Ref, Namespace);
Temp2 := TValue.FromVariant(Temp1);
Temp2 := Temp2.Convert(RTTIField.FieldType.Handle{, FormatSettings}); // in Spring.TValueHelper
RTTIField.SetValue(Value, Temp2);
end;
Upvotes: 1
Views: 672
Reputation: 21713
The built-in type casts in TValue will not help you here as they only allow those types that are explicitly compatible (i.e. assignable). Technically if you store the Variant inside the TValue without "unpacking" it which is what FromVariant
does internally it should be able to cast the Variant to anything it usually can be cast/converted to. However there are is at least one issue with casting a Variant holding 'True' or 'False' to a Boolean (see https://quality.embarcadero.com/browse/RSP-20160)
However since you are already using Spring4D you can use its improved TValue type conversion feature.
Just use the Convert
method from the TValueHelper
in Spring.pas
.
There you can pass a PTypeInfo (which would be Left.FieldType.Handle
in your code) and optionally a TFormatSettings
- by default it will use the current locale.
Upvotes: 3
Reputation: 34909
What is the way to transition values between the Variant and the RTTI world?
Use the built in class function conversion in System.RTTI.TValue:
myTValue := TValue.FromVariant(myVariant);
Builds a new TValue record from a Variant value.
FromVariant is a static method that can be used to build TValue records with a stored Variant value. The Value parameter contains the Variant that will be stored inside the built TValue record.
Upvotes: 2