kabstergo
kabstergo

Reputation: 771

Strange PTypeInfo name for generic integer

I need to do a "smart" detection of a generic type and return as a string, but at the moment i don't understand why delphi put some strange identification on the PTypeInfo.Name property.

What i have so far is this:

program SO_29674887;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils,
  System.TypInfo;

type
  TypeResolver = class
  strict private
    class function ReCast<T>(const AValue) : T;
  public
    class function Format<T>(const AValue : T) : string;
  end;

  Compare = class
  public
    class function IsEqual<A;B>(const AVal : A; BVal : B) : string;
  end;

{ TypeResolver }

class function TypeResolver.ReCast<T>(const AValue): T;
begin
  Result := T(AValue);
end;

class function TypeResolver.Format<T>(const AValue: T): string;
var Info : PTypeInfo;
begin
  Info := TypeInfo(T);

  Result := 'undefined';

  if(Info.Kind = tkInteger) then
    begin
      if(Info.Name = GetTypeName(TypeInfo(Byte))) then
        Result := IntToStr(ReCast<Byte>(AValue))
      else if(Info.Name = GetTypeName(TypeInfo(ShortInt))) then
        Result := IntToStr(ReCast<ShortInt>(AValue))
      else if(Info.Name = GetTypeName(TypeInfo(SmallInt))) then
        Result := IntToStr(ReCast<SmallInt>(AValue))
      else if(Info.Name = GetTypeName(TypeInfo(Integer))) then
        Result := IntToStr(ReCast<Integer>(AValue));
    end;

  Result := Info.Name + ':' + Result;
end;

{ Compare }

class function Compare.IsEqual<A, B>(const AVal: A; BVal: B): string;
begin
  Result := Format('%s = %s', [
    TypeResolver.Format<A>(AVal),
    TypeResolver.Format<B>(BVal)]);
end;

var PAUSE : string;

begin
  try
    { TODO -oUser -cConsole Main : Insert code here }
    WriteLn(Compare.IsEqual(0, 255));
    WriteLn(Compare.IsEqual(-127, 127));
    WriteLn(Compare.IsEqual(0, 65535));
    WriteLn(Compare.IsEqual(-32768, 32767));
    WriteLn(Compare.IsEqual(0, 4294967295));
    WriteLn(Compare.IsEqual(-2147483648, 2147483647));
    Readln(PAUSE);
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

What happens here is that instead of give me the actual type name of the integer, delphi gives me strange identifier like :2, :4, :6, etc.

For instance:

Compare.IsEqual(0, 255); //Gives Info.Name = :2, Byte
Compare.IsEqual(-127, 127); //Gives Info.Name = ShortInt, :4
Compare.IsEqual(0, 65535); //Gives Info.Name = :6, Word
Compare.IsEqual(-32768, 32767); //Gives Info.Name = SmallInt, :8
Compare.IsEqual(0, 4294967295); //Gives Info.Name = :01, Cardinal
Compare.IsEqual(-2147483648, 2147483647); //Gives Info.Name = Integer, :21

So my question is how i can find the right type to cast if it seems to me that delphi doesn't provide any clue of the actual type when he deliver those identifiers and why exactly it gives those odd identifiers.

Upvotes: 4

Views: 747

Answers (2)

David Heffernan
David Heffernan

Reputation: 612794

Here's my reproduction, somewhat shorter.

{$APPTYPE CONSOLE}

uses
  System.TypInfo;

type
  TypeResolver = class
  public
    class function Format<T>(const AValue : T) : string;
  end;

class function TypeResolver.Format<T>(const AValue: T): string;
var
  Info: PTypeInfo;
begin
  Info := TypeInfo(T);
  Result := Info.Name;
end;

begin
  Writeln(TypeResolver.Format(0));
  Writeln(TypeResolver.Format<Byte>(0));
  Writeln(TypeResolver.Format(255));
  Readln;
end.

In XE2 the output is:

:3
Byte
Byte

In XE6 and later the output is:

ShortInt
Byte
Byte

It looks as though the earlier versions of Delphi create a private type with an unspeakable name when inferring from a literal of 0. I cannot say why that should be so. Since the behaviour has changed, one can only assume that the Embarcadero engineers made the change to fix what they deemed to be a defect.

In other words, it would seem that the behaviour that you are observing is a bug.

My hypothesis that a private type is created is backed up by this program:

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  System.TypInfo;

type
  TypeResolver = class
  public
    class function Format<T>(const AValue : T) : string;
  end;

class function TypeResolver.Format<T>(const AValue: T): string;
var
  Info: PTypeInfo;
  TypeData: PTypeData;
begin
  Info := TypeInfo(T);
  Result := Info.Name;
  if Info.Kind=tkInteger then begin
    TypeData := GetTypeData(Info);
    Result := Result + ', min = ' + IntToStr(TypeData.MinValue) + 
      ', max = ' + IntToStr(TypeData.MaxValue);
  end;
end;

begin
  Writeln(TypeResolver.Format(0));
  Readln;
end.

On XE2 the output is:

:3, min = 0, max = 127

On XE6 the output is:

ShortInt, min = -128, max = 127

So I think that this is an issue with generic type inference, that we can probably ascribe to a bug fixed in XE6.

I don't have any advice for how you should work around this because I don't know your actual problem. That said, comparing type names is generally something that is best avoided if possible.

Upvotes: 4

Jens Borrisholt
Jens Borrisholt

Reputation: 6402

It seems for me like you are trying to invent TValue which is located in RTTI.pas

I've rewritten your example using TValue.

Place a TMemo on a form and the following code:

uses
  RTTI;

type
  TypeResolver = class
  public
    class function ReCast<T>(const AValue): T;
    class function Format<T>(const AValue: T): string;
  end;

  Compare = class
  public
    class function IsEqual<A; B>(const AValA: A; const AValB: B): string;
  end;

class function Compare.IsEqual<A, B>(const AValA: A; const AValB: B): string;
begin
  Result := Format('%s = %s', [TypeResolver.Format<A>(AValA), TypeResolver.Format<B>(AValB)]);
end;

{ TypeResolver }

class function TypeResolver.ReCast<T>(const AValue): T;
begin
  Result := T(AValue);
end;

class function TypeResolver.Format<T>(const AValue: T): string;
begin
  Result := TValue.From(AValue).TypeInfo.Name;
end;

procedure TForm13.FormCreate(Sender: TObject);
begin
  Memo1.Lines.Add(Compare.IsEqual(0, 255));
  Memo1.Lines.Add(Compare.IsEqual(-127, 127));
  Memo1.Lines.Add(Compare.IsEqual(0, 65535));
  Memo1.Lines.Add(Compare.IsEqual(-32768, 32767));
  Memo1.Lines.Add(Compare.IsEqual(0, 4294967295));
  Memo1.Lines.Add(Compare.IsEqual(-2147483648, 2147483647));
  Memo1.Lines.Add(Compare.IsEqual(Form13, Memo1));
end;

I belive it does the thick for you?

Here is the output of above:

ShortInt = Byte
ShortInt = ShortInt
ShortInt = Word
SmallInt = SmallInt
ShortInt = Cardinal
Integer = Integer
TForm13 = TMemo

Upvotes: 4

Related Questions