Converting value of type <T> into Variant, is it possible?
Asked Answered
T

4

6

here is a snippet showing what I am trying to achieve:

type
  TMyObject<T> = class (TObject)
    function GetVarType(Value: T): TVarType;
  end;


function TMyObject<T>.GetVarType(Value: T): TVarType;
var
  TmpValue: Variant;
begin
  TmpValue := Variant(Value); //Invalid typecast
  Result := VarType(TmpValue);
end;

I know that above apporach with typecast is naive but I hope you get the idea. I would like to replace it with some conversion mechanism.

TMyObject will be always of simple type like Integer, String, Single, Double.

The purpose of such conversion is that function VarType gives me integer constant for each simple type which I can store somewhere else.

I would like to know if such conversion is possible?

Thanks for your time.

Tarlatan answered 25/10, 2011 at 15:55 Comment(0)
S
6

You can use the RTTI to get this info, just check the value of the TTypeInfo.Kind property:

Check this sample code

{$APPTYPE CONSOLE}

uses
  TypInfo,
  Variants,
  Generics.Collections,
  SysUtils;

type
  TMyObject<T> = class (TObject)
    function GetVarType(Value: T): TVarType;
  end;


function TMyObject<T>.GetVarType(Value: T): TVarType;
begin
  Case PTypeInfo(TypeInfo(T))^.Kind of
    tkInteger : Result:=varInteger;
    tkFloat   : Result:=varDouble;
    tkString  : Result:=varString;
    tkUString : Result:=varUString;
    //add more types here
  End;
end;

Var
  LObj : TMyObject<Integer>;
begin
  try
     Writeln(VarTypeAsText(TMyObject<Integer>.Create.GetVarType(5)));
     Writeln(VarTypeAsText(TMyObject<String>.Create.GetVarType('Test')));
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

this will return

Integer
UnicodeString
Snooze answered 25/10, 2011 at 16:15 Comment(0)
M
7

It's trivially solvable in Delphis with enhanced RTTI (2010 and newer). Too bad you're limited to 2009 :(

function TMyObject<T>.GetVarType(Value: T): TVarType;
begin
  Result := VarType(TValue.From<T>(Value).AsVariant);
end;

This works only for simple types but that was a constraint specified in the question.

Marler answered 25/10, 2011 at 21:49 Comment(1)
Thanks @Marler it will be definitely helpful when I switch to D2010 :)Tarlatan
S
6

You can use the RTTI to get this info, just check the value of the TTypeInfo.Kind property:

Check this sample code

{$APPTYPE CONSOLE}

uses
  TypInfo,
  Variants,
  Generics.Collections,
  SysUtils;

type
  TMyObject<T> = class (TObject)
    function GetVarType(Value: T): TVarType;
  end;


function TMyObject<T>.GetVarType(Value: T): TVarType;
begin
  Case PTypeInfo(TypeInfo(T))^.Kind of
    tkInteger : Result:=varInteger;
    tkFloat   : Result:=varDouble;
    tkString  : Result:=varString;
    tkUString : Result:=varUString;
    //add more types here
  End;
end;

Var
  LObj : TMyObject<Integer>;
begin
  try
     Writeln(VarTypeAsText(TMyObject<Integer>.Create.GetVarType(5)));
     Writeln(VarTypeAsText(TMyObject<String>.Create.GetVarType('Test')));
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

this will return

Integer
UnicodeString
Snooze answered 25/10, 2011 at 16:15 Comment(0)
A
1

I can't see how it could be possible to do this with generics. The compiler needs to know that an instance of type T can be assigned to a Variant for any possible T. There's no way for you to tell that compiler that is possible.

If this were templates as in C++ then it would be trivial.

Ashtoreth answered 25/10, 2011 at 16:6 Comment(2)
@seth I didn't know you were a delphi user. I always had you down as a curly braces kinda guy!Ashtoreth
Usually but not exclusively. I use Delphi as a native-executable-generating version of C# (can't stand not being able to use online assembly) and like it a lot.Picture
T
1

Thanks guys for your answers:

As @RRUZ have shown it is possible (I mean not strict assigment but extracting the type of data). I was working on my own while waiting for any answer and have found a more generic solution.

So I am positing it here:

type
  TMyObject<T> = class (TObject)
    function GetVarType(Value: T): TVarType;
  end;


function TMyObject<T>.GetVarType(Value: T): TVarType;
begin
  Result := GetTypeData(TypeInfo(T)).varType;
end;

Once again thanks!

Tarlatan answered 25/10, 2011 at 16:25 Comment(5)
Is there any indication that varType contains a valid value when TypeInfo(T).Kind isn't tkDynArray? Or that the integer stored in varType is really a TVarType value, since dynamic arrays can hold things that Variant can't? I don't think either are necessarily true.Bennet
@RobKennedy I am afraid you have right. I don't get why it is not working as expected. That is a shame...Tarlatan
It's not working as expected because of the issues I pointed out before. The value you're reading doesn't contain the data type you want, isn't always valid, and doesn't even represent what you thought it did in the first place. (It represents the element type of a dynamic array, not the type of T itself, which would be array of varType.)Bennet
@Rob I don't get it, if I crate TMyObject<T> in this way: MyObject := TMyObject<Integer>.Create and I pass an integer to GetVarType function, then why T is not representing an integer but some element of dynamic array?Tarlatan
I didn't say T was an element of a dynamic array. TTypeInfo represents the type information for a certain type. When that type is a dynamic array, the TTypeInfo.varType field is valid and holds the array's element type. For example, if T were array of Char, varType still wouldn't give you the desired answer. Instead of an integer meaning "array of Char," you'd get an integer meaning "Char."Bennet

© 2022 - 2024 — McMap. All rights reserved.