Reputation: 771
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
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
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