Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 393
kabstergo Avatar asked Mar 17 '23 10:03

kabstergo


1 Answers

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.

like image 191
David Heffernan Avatar answered Mar 24 '23 17:03

David Heffernan