* * *

Author Topic: Enum nested classes in a class  (Read 1755 times)

Zaher

  • Hero Member
  • *****
  • Posts: 560
    • parmaja.com
Enum nested classes in a class
« on: June 27, 2018, 02:37:30 am »
I want to enumerate/get list of classes that nested inside a parent class, is there any way using RTTI or something else?

Without manually adding to list.
 
Code: [Select]
  TParentClass = class(TObject)
  protected
    type

      TNestedClass = class(TObject)
      public
      end;

      TAnotherNestedClass = class(TObject)
      public
      end;
  end;

taazz

  • Hero Member
  • *****
  • Posts: 5344
Re: Enum nested classes in a class
« Reply #1 on: June 27, 2018, 02:57:20 am »
hopefully not, two main reasons 1) tobject does not support RTTI you need to inherit from TPersistent or above to allow that and 2) if its nested inside an other class then no outside code should be able to know or find out any information about the type including RTTI, unless the types are declared as public in which case don't make them nested.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

PascalDragon

  • Full Member
  • ***
  • Posts: 203
  • Compiler Developer
Re: Enum nested classes in a class
« Reply #2 on: June 27, 2018, 03:44:31 pm »
hopefully not, two main reasons 1) tobject does not support RTTI you need to inherit from TPersistent or above to allow that and 2) if its nested inside an other class then no outside code should be able to know or find out any information about the type including RTTI, unless the types are declared as public in which case don't make them nested.
Of course TObject supports RTTI. The only difference of TPersistent is that it enables published properties using {$M+}.
However even then enumerating classes inside another class is not possible. In the future the type list of unit Rtti can be used, though one manually needs to check for a "parent" class.

Zaher

  • Hero Member
  • *****
  • Posts: 560
    • parmaja.com
Re: Enum nested classes in a class
« Reply #3 on: June 27, 2018, 07:18:13 pm »
In the future the type list of unit Rtti can be used

So it still not exists yet?

Thaddy

  • Hero Member
  • *****
  • Posts: 7136
Re: Enum nested classes in a class
« Reply #4 on: June 27, 2018, 07:39:10 pm »
If you are "persistent" enough and use your imagination or knowledge of some internals of the language it is already possible....
Just requires an explicit interface, but some people are irrationally allergic to such code. Sven is going to hide that from us... :D 8-) O:-)
inline variables like in D10.3 are a bit like Brexit: if you are given the wrong information it sounds like a good idea. Every kid loves candy, but it makes you fat and your teeth will disappear.

Zaher

  • Hero Member
  • *****
  • Posts: 560
    • parmaja.com
Re: Enum nested classes in a class
« Reply #5 on: June 27, 2018, 08:00:59 pm »
I can change to use persistent, it is easy, but i need a tip to start searching for it.
Also i want it compatible with Delphi.

Thaddy

  • Hero Member
  • *****
  • Posts: 7136
Re: Enum nested classes in a class
« Reply #6 on: June 27, 2018, 08:48:34 pm »
I can change to use persistent, it is easy, but i need a tip to start searching for it.
Also i want it compatible with Delphi.
well, I can provide you with an example, but as far as Delphi compatibility goes: FPC is actively developed. Delphi is not! (the fpc core team in now about 800% larger than the delphi core "team" and they do it in their spare time.).
« Last Edit: June 27, 2018, 08:50:12 pm by Thaddy »
inline variables like in D10.3 are a bit like Brexit: if you are given the wrong information it sounds like a good idea. Every kid loves candy, but it makes you fat and your teeth will disappear.

Zaher

  • Hero Member
  • *****
  • Posts: 560
    • parmaja.com
Re: Enum nested classes in a class
« Reply #7 on: June 28, 2018, 08:58:26 pm »
Ok let us drop "Also i want it compatible with Delphi."

PascalDragon

  • Full Member
  • ***
  • Posts: 203
  • Compiler Developer
Re: Enum nested classes in a class
« Reply #8 on: June 28, 2018, 10:20:21 pm »
In the future the type list of unit Rtti can be used

So it still not exists yet?

It exists in trunk, but it's still incomplete (including some missing binary data) and currently considered experimental.

engkin

  • Hero Member
  • *****
  • Posts: 2116
Re: Enum nested classes in a class
« Reply #9 on: June 30, 2018, 09:26:17 pm »
Ok let us drop "Also i want it compatible with Delphi."

While waiting for Thaddy, here is my crime:
Code: Pascal  [Select]
  1. program project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   typinfo;
  7.  
  8. type
  9.   TParentClass = class(TObject)
  10.   protected
  11.     type
  12.  
  13.       TNestedClass = class(TObject)
  14.       public
  15.       end;
  16.  
  17.       TAnotherNestedClass = class(TObject)
  18.       public
  19.       end;
  20.   end;
  21.  
  22.   {$PACKRECORDS 1}
  23.   { *** Memory Layout ***
  24.   //Header TTypeInfo then: }
  25.  
  26.   TClassInfo = record // from typinfo.TTypeData for classes
  27.     ClassType: TClass;
  28.     ParentInfo: PTypeInfo;
  29.     PropCount: SmallInt; { including parents }
  30.   end;
  31.  
  32.   { then:
  33.     UnitName: ShortString;  { variable length }
  34.  
  35.     { TPropInfo }
  36.     thisClassCount: word; { excluding parents }
  37.     here the properties follow as array[0..thisClassCount-1] of TPropInfo. Check typInfo.GetPropInfos
  38.  
  39.     *** End of Memory Layout ***
  40.     }
  41.  
  42.   PClassInfo = ^TClassInfo;
  43.  
  44. type
  45.   TClasses=array of TClass;
  46.  
  47. function EnumNestedClasses(AMainClass: TClass; out AClasses: TClasses): integer;
  48. var
  49.   p: pByte;
  50.   PossibleInfo: PTypeInfo;
  51.   ClassInfo: PClassInfo;
  52.   i: Integer;
  53.   found: Boolean;
  54. begin
  55.   Result := 0;
  56.   PossibleInfo := PTypeInfo(AMainClass.ClassInfo);
  57.   if PossibleInfo^.Kind=tkClass then
  58.   begin
  59.     p := Pointer(PossibleInfo);
  60.     p := p - SizeOf(TClassInfo) - Length(AMainClass.UnitName);
  61.     repeat
  62.       found := False;
  63.       for i := 1 to 255 do
  64.       begin
  65.         dec(p);
  66.         if (PByte(p-1)^=Byte(tkClass)) then
  67.         begin
  68.           PossibleInfo := PTypeInfo(p-1);
  69.           ClassInfo := PClassInfo(pByte(@PossibleInfo^.name)+Length(PossibleInfo^.name)+1);
  70.  
  71.           try
  72.             if (ClassInfo^.ClassType.UnitName<>AMainClass.UnitName) { same unit }
  73.                or (Length(AMainClass.ClassName) >= Length(ClassInfo^.ClassType.ClassName)) { Parent name is included }
  74.                or (ClassInfo^.ClassType.ClassName[Length(AMainClass.ClassName)+1]<>'.') { Of form: ParentClsName.ChildClsName }
  75.                or (Pos(AMainClass.ClassName, ClassInfo^.ClassType.ClassName)<>1)
  76.                  then continue;
  77.           except
  78.             continue; { access violation }
  79.           end;
  80.  
  81.           found := True;
  82.  
  83.           inc(Result);
  84.           SetLength(AClasses, Result);
  85.           AClasses[Result-1] := ClassInfo^.ClassType;
  86.  
  87.           Break;
  88.         end;
  89.       end;
  90.       p := pointer(PossibleInfo)-1;
  91.     until not found;
  92.   end;
  93. end;
  94.  
  95. var
  96.   NestedClasses: TClasses;
  97.   NestedClass: TClass;
  98.  
  99. begin
  100.   if Length(TParentClass.TAnotherNestedClass.ClassName)>Length(TParentClass.TNestedClass.ClassName) then ;//to force the compiler to include the classes.
  101.  
  102.   EnumNestedClasses(TParentClass, NestedClasses);
  103.   try
  104.     for NestedClass in NestedClasses do
  105.       WriteLn(NestedClass.ClassName);
  106.   finally
  107.     SetLength(NestedClasses, 0);
  108.   end;
  109.   ReadLn;
  110. end.

Nested classes must be used for the compiler to include their data.
Does not matter what class they descent from.
Caller responsible to free the array of classes.
Works with FPC 3.0.4.
Tested on Win32.

PascalDragon

  • Full Member
  • ***
  • Posts: 203
  • Compiler Developer
Re: Enum nested classes in a class
« Reply #10 on: July 02, 2018, 08:40:42 pm »
Please note that this will likely break if a class contains properties, especially once Extended RTTI is integrated. Not to mention that it is in no way guaranteed that classes are written in that order.

engkin

  • Hero Member
  • *****
  • Posts: 2116
Re: Enum nested classes in a class
« Reply #11 on: July 04, 2018, 02:50:05 pm »
Please note that this will likely break if a class contains properties, especially once Extended RTTI is integrated. Not to mention that it is in no way guaranteed that classes are written in that order.
I called it "crime" as no one should need to use something like that. I assume the compiler will provide a better mechanism in the near future ;-)

I did mention the class properties in the memory layout, but yes, did not account for them. Adding class properties should be easy considering that Name is the only variable part of the record and located at the end, and knowing that all Pascal identifiers are limited to ['A'..'Z','a'..'z','0'..'9','_'] and does not start with a number. NameIndex could help as well. So finding identifier names could use something like:
Code: Pascal  [Select]
  1. procedure RSearchIdentifierName(var p: pByte);
  2. const
  3.   AllowedChars: set of char = ['A'..'Z','a'..'z','0'..'9','_'];
  4. var
  5.   lastChar: pByte;
  6. begin
  7. {
  8. Identifiers consist of between 1 and 127 significant characters
  9. (letters, digits and the underscore character),
  10. of which the first must be a letter (a-z or A-Z), or an underscore (_).
  11. }
  12.   repeat
  13.     { Skip non-allowed chars }
  14.     while not (char(p^) in AllowedChars) do
  15.       dec(p);
  16.  
  17.     lastChar := p;
  18.  
  19.     { Find possible beginning}
  20.     while true do
  21.     begin
  22.       if (p+p^<=lastChar) then exit;
  23.       if not (char(p^) in AllowedChars) then
  24.         break;
  25.       dec(p);
  26.     end;
  27.   until false;
  28. end;

to find classes, then we need to check the byte preceding the possible identifier name for tkClass, then we have more means to make sure we landed on a class.

Repeating the process forward is easier than looking in reverse direction. OTOH, scanning the whole .data section, where our classes are kept, is also possible.

As for breakage, you guys can, rightfully, cause this code to break. And we will have to fix it. Luckily, that does not happen frequently.

Honestly, I posted that code to get Thaddy to show us his example. Alas, I failed. :-|

PascalDragon

  • Full Member
  • ***
  • Posts: 203
  • Compiler Developer
Re: Enum nested classes in a class
« Reply #12 on: July 06, 2018, 03:35:20 pm »
I did mention the class properties in the memory layout, but yes, did not account for them. Adding class properties should be easy considering that Name is the only variable part of the record and located at the end, and knowing that all Pascal identifiers are limited to ['A'..'Z','a'..'z','0'..'9','_'] and does not start with a number. NameIndex could help as well.

Please note that the statement that Pascal identifiers are limited to that set of characters is wrong as soon as Unicode identifiers are supported which will result in UTF8 identifier names. (Note: it's currently not planned, so just as a heads up)

engkin

  • Hero Member
  • *****
  • Posts: 2116
Re: Enum nested classes in a class
« Reply #13 on: July 06, 2018, 09:30:04 pm »
Please note that the statement that Pascal identifiers are limited to that set of characters is wrong as soon as Unicode identifiers are supported which will result in UTF8 identifier names. (Note: it's currently not planned, so just as a heads up)

That would be fantastic news:
  😁 := 😊 + 1;

UTF8 identifier names are easy to find because of UTF8 itself. Meanwhile according to the current docs:
Quote
Identifiers consist of between 1 and 127 significant characters (letters, digits and the underscore character), of which the first must be a letter (a-z or A-Z), or an underscore (_).

 

Recent

Get Lazarus at SourceForge.net. Fast, secure and Free Open Source software downloads Open Hub project report for Lazarus