Max
Max

Reputation: 701

How to assign an OleVariant with RTTI? // Convert an OleVariant or a Variant to a TValue with specific TTypeKind or TRTTIType in mind?

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

Answers (2)

Stefan Glienke
Stefan Glienke

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

LU RD
LU RD

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

Related Questions