Jump to content
Sign in to follow this  
Mahdi Safsafi

Unnamed types and RTTI

Recommended Posts

Hello,

Take a look at the code/outputs bellow :


type
  TMyClass = class
    FldEnum: (A, B, C);
    FldSet: set of (D, E, F);
    FldSubRange: 5 .. 10;

    FldRec: record
      FA: Integer;
      FB: Integer;
    end;

    FldInteger: Integer;
    FldString: string;
    FldArray: array [0 .. 2] of Integer;
    FldList: TList < (G, H, I) >;

    FldArrayOfRec: array [0 .. 2] of record A: Char;
    B: Char;
  end;
end;

type
  TMyClass2<T> = class(TMyClass)
    FldEnum: (A2, B2, C2);
    FldSet: set of (D2, E2, F2);
    FldSubRange: 5 .. 10;

    FldRec: record
      FA: Integer;
      FB: Integer;
    end;

    FldInteger: Integer;
    FldString: string;
    FldArray: array [0 .. 2] of Integer;
    FldList: TList < (G2, H2, I2) >;

    FldArrayOfRec: array [0 .. 2] of record A: Char;
    B: Char;
  end;
end;

procedure ShowRtti(AObj: TObject);
var
  LCtx: TRttiContext;
  LType: TRttiType;
  LField: TRttiField;
  LFieldType: TRttiType;
begin
  LCtx := TRttiContext.Create();
  LType := LCtx.GetType(AObj.ClassInfo);
  Writeln('------------ RTTI for ', AObj.ToString, ' ------------');
  for LField in LType.GetFields() do
  begin
    LFieldType := LField.FieldType;
    if Assigned(LFieldType) then
    begin
      Writeln(LField.Name:15, ' -> ', LFieldType.Name);
    end;
  end;
  Writeln('');
  LCtx.Free();
end;

var
  Obj1: TMyClass;
  Obj2: TMyClass2<Integer>;
begin
  Obj1 := TMyClass.Create();
  Obj2 := TMyClass2<Integer>.Create();
  ShowRtti(Obj1);
  ShowRtti(Obj2);
  Obj1.Free();
  Obj2.Free();
  Readln;
end.

// --- outputs ---
{
------------ RTTI for TMyClass ------------
        FldEnum -> :TMyClass.:1
         FldRec -> :TMyClass.:3
     FldInteger -> Integer
      FldString -> string
        FldList -> TList<Project1.:TMyClass.:4>

------------ RTTI for TMyClass2<System.Integer> ------------
        FldEnum -> TMyClass2<System.Integer>.:1
         FldSet -> TMyClass2<System.Integer>.:3
    FldSubRange -> TMyClass2<System.Integer>.:4
         FldRec -> TMyClass2<System.Integer>.:5
     FldInteger -> Integer
      FldString -> string
       FldArray -> TMyClass2<System.Integer>.:7
        FldList -> TList<Project1.TMyClass2<System.Integer>.:8>
  FldArrayOfRec -> TMyClass2<System.Integer>.:11
        FldEnum -> :TMyClass.:1
         FldRec -> :TMyClass.:3
     FldInteger -> Integer
      FldString -> string
        FldList -> TList<Project1.:TMyClass.:4>
}

As you can see, for TMyClass, some unnamed types(record, enum,) have associated RTTI. But types such (subrange, sets, array) don't have ! On the other hand, all fields of TMyClass2 have associated RTTI. 

This is definitely a bug as the compiler should only accept one behavior(either enables RTTI for all unnamed types or disables them).  ... but the question I'm asking is what is the correct behavior ? In other word, should an unnamed type have RTTI or not ?  

All typed languages I'm familiar with solved this by not allowing unnamed/anonymous types. C/C++ are an exception ! They allow both unnamed and anonymous types but they don't have RTTI system (at least an advanced system like Delphi). So its kind hard to know whats the correct behavior when there is no reference around. BTW, I'd love to see how FPC is handling it.

In my opinion, I think that compiler should generate RTTI for unnamed types ... But when I think deeply I say no ! this is an unnamed type (most likely to be anonymous. You declared it implicitly ... why you should expect to have explicit RTTI in return ?)

Please guys, I'm not asking for a workaround/good practice/historical reason ... just focus on the question 🙂 
 

Share this post


Link to post
Quote

What really does disturb me the most is FldList.

Wow ! I missed that ... I'll investigate and let you know. 

Quote

Mahdi, are you familiar with DCU decompiler ?

No, but sounds interesting ... I'll take a look later.

Share this post


Link to post
24 minutes ago, Mahdi Safsafi said:
Quote

Mahdi, are you familiar with DCU decompiler ?

No, but sounds interesting ... I'll take a look later.

I think it will give you faster look under the hood, i know you will like peeking under the hood 😉 , i don't mean that in perverted way, me too likey looking under the hood !!

 

Just drag and drop the dcu on that Decompiler/Interface parser , here short of the output ( i use Notpad++ with such files, it does have Pascal coloring and save IDE from too much thinking)

// parts from the output
type
  TMyClass = class(TObject) 
  public
    FldEnum: (A, B, C);
    FldSet: set of (D, E, F);
    FldSubRange: $5..$A;
    FldRec: record 
      FA: Integer;
      FB: Integer;
    end;
    FldInteger: Integer;
    FldString: UnicodeString;
    FldArray: array[$0..$2] of Integer;
    FldList: {System.Generics.Collections}TList<Unit1.:TMyClass.:4>;
    FldArrayOfRec: array[$0..$2] of record 
        A: Char;
        B: Char;
      end;
  end;

  {System}TArray<Unit1.:TMyClass.:4>{TArray`1<(G, H, I)>} = array of (G, H,
    I);
////
  TMyClass2`1 = class(TMyClass) 
  public
    FldEnum: :TMyClass2`1.:1<T>;
    FldSet: set of :TMyClass2`1.:2<T>;
    FldSubRange: $5..$A;
    FldRec: :TMyClass2`1.:5<T>;
    FldInteger: Integer;
    FldString: UnicodeString;
    FldArray: {Unit1}TMyClass2<T>.:7;
    FldList: {System.Generics.Collections}TList<Unit1.TMyClass2<Unit1.TMyClass2<T>.T>.:8>;
    FldArrayOfRec: {Unit1}TMyClass2<T>.:11;
  end;
////
type
  {Unit1}TMyClass2<T>.:7{array[$0..$2] of Integer<T>} = array[$0..$2] of
    Integer;

  {Unit1}TMyClass2<T>.:11{array[$0..$2] of
    (Unit1)TMyClass2<T>.:01<T>} = array[$0..$2] of {Unit1}TMyClass2<T>.:01;

  {Unit1}TMyClass2<T>{class(TMyClass) 
  public
    FldEnum: :TMyClass2`1.:1<T>;
    FldSet: set of :TMyClass2`1.:2<T>;
    FldSubRange: $5..$A;
    FldRec: :TMyClass2`1.:5<T>;
    FldInteger: Integer;
    FldString: UnicodeString;
    FldArray: (Unit1)TMyClass2<T>.:7;
    FldList: (System.Generics.Collections)TList<Unit1.TMyClass2<Unit1.TMyClass2<T>.T>.:8>;
    FldArrayOfRec: (Unit1)TMyClass2<T>.:11;
  end<T>} = class(TMyClass) 
  public
    FldEnum: :TMyClass2`1.:1<T>;
    FldSet: :TMyClass2`1.:3<T>;
    FldSubRange: :TMyClass2`1.:4<T>;
    FldRec: :TMyClass2`1.:5<T>;
    FldInteger: Integer;
    FldString: string;
    FldArray: {Unit1}TMyClass2<T>.:7;
    FldList: {System.Generics.Collections}TList<Unit1.TMyClass2<Unit1.TMyClass2<T>.T>.:8>;
    FldArrayOfRec: {Unit1}TMyClass2<T>.:11;
  end;

  {Unit1}TMyClass2<T>.:01{record 
    A: Char;
    B: Char;
  end<T>} = record 
    A: Char;
    B: Char;
  end;

To @Daniel Notepad++ is handling the pascal format coloring for comments '{}' better this Insert code, you may need to look at that, i know it is very not important, but don't we all like perfection like many other things ? (e.g. peeking under the hood)

Praxis_Pascal.thumb.png.878a1f8f6b7df2170991f546d100e488.png

Share this post


Link to post

@Kas Ob. for FldList, it sounds Ok. Compiler used Integer for just the name ... all other RTTI information are valid (e.g: MaxValue).

BTW, Notepad++ provides a user defined language. You can quickly make your pascal-variant 😉

 

  • Thanks 1

Share this post


Link to post

Undeclared types probably do not have RTTI because it kinda pointless to be looking up an unnamed type using RTTI, since you don't have a name to look up.

Besides, if you were using unnamed types in a class in production code, I'd fire you from my team.

  • Haha 1

Share this post


Link to post
15 minutes ago, Lars Fosdal said:

Besides, if you were using unnamed types in a class in production code, I'd fire you from my team.

So, the one(s) who will allow the compiler to compile such not-OK-with-Lars should be classified as fire-proof or not fire-proof ? 😎

  • Haha 1

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×