Fabrizio
Fabrizio

Reputation: 8053

Converting a generic type variable to string

I'm trying to convert a generic variable of type T into string.

  TMyTest = class
    class function GetAsString<T>(const AValue : T) : string; static;
  end;

...

uses
  System.Rtti;

class function TMyTest.GetAsString<T>(const AValue : T) : string;
begin
  Result := TValue.From<T>(AValue).ToString();
end;

It works good using several types (like Integer, Double, Boolean...) but it "fails" using Variant variables.

procedure TForm1.FormCreate(Sender: TObject);
var
  Tmp : Variant;
begin
  Tmp := 123;

  ShowMessage(TMyTest.GetAsString<Variant>(Tmp));
end;

It produces the following output:

(variant)

I was expecting the same output obtained by the VarToStr function (But I cannot use that function with generic variables):

123

Upvotes: 4

Views: 2142

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 598329

You can check the type of T using RTTI, and then call VarToStr() when T is a Variant, eg:

class function TMyTest.GetAsString<T>(const AValue : T) : string;
begin
  if TypeInfo(T) = TypeInfo(Variant) then begin
    // yes, the following cast looks odd, but the compiler can't validate
    // T is really a Variant in this context, as it syntax-checks the code 
    // *before* instantiating the Generic, so a runtime cast is needed.
    // The TypeInfo check above will ensure the cast is safe...
    Result := VarToStr({AValue}PVariant(@AValue)^);
  end else begin
    Result := TValue.From<T>(AValue).ToString;
  end;
end;

Or, in XE7+, you can use the GetTypeKind() intrinsic instead:

class function TMyTest.GetAsString<T>(const AValue : T) : string;
begin
  if GetTypeKind(T) = tkVariant then begin
    Result := VarToStr({AValue}PVariant(@AValue)^);
  end else begin
    Result := TValue.From<T>(AValue).ToString;
  end;
end;

Either approach will allow the compiler to optimize the code by dropping the unused branch from the executable, as both comparisons are treated as compile-time constants.


Note: other TTypeKind values types that TValue.ToString does not support are tkUnknown, tkArray, tkRecord, and tkDynArray.

Upvotes: 3

Dalija Prasnikar
Dalija Prasnikar

Reputation: 28541

You can check whether T is variant and then use VarToStr on AsVariant function.

You can easily extend that function to cater for other types where ToString will not give you expected result.

uses
  System.TypInfo, System.Rtti;

class function TMyTest.GetAsString<T>(const AValue : T) : string;
begin
  if PTypeInfo(TypeInfo(T)).Kind = tkVariant then
    Result := VarToStr(TValue.From<T>(AValue).AsVariant)
  else
    Result := TValue.From<T>(AValue).ToString();
end;

Upvotes: 6

Related Questions