Recent

Author Topic: Helpers for TObject  (Read 716 times)

damieiro

  • Jr. Member
  • **
  • Posts: 61
Helpers for TObject
« on: December 05, 2018, 10:12:05 am »
Hi!

I have in mind to make class helpers for .AfterConstruction and .BeforeDestruction methods in TObject to make some accounting of class construction, destruction and debugging if some conditional compilation is on.

Actually i have a Descendant class from TObject that does that and all my classes descend from it.

But from a Tobject helper, i could see all classes, not only mines.

The question is:

Is advisable doing that? Could have any problems?. Actually these methods are empty ones.

I have not tested or even starting to code with that option...


marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 6617
Re: Helpers for TObject
« Reply #1 on: December 05, 2018, 11:20:28 am »
Helpers don't modify the class. It only allows you to call "extra" methods on the class if the helper is in view.

So I don't think this will work.

sash

  • Full Member
  • ***
  • Posts: 158
Re: Helpers for TObject
« Reply #2 on: December 05, 2018, 11:22:16 am »
It seems like you want to override virtual methods of class with helper. It is impossible and conflicts with helper concept.
Helpers are supposed to be an addons to existing code, not a replacements (like overridden virtual methods).

AfterConstruction, BeforeDestruction are regular virtual (and public) methods. Why don't you simply override them for interested classes?

What's your exact goal?
Lazarus 1.8.4 Unversioned directory FPC 3.0.4 x86_64-linux-gtk2 -- Ubuntu 18.04 XFCE

damieiro

  • Jr. Member
  • **
  • Posts: 61
Re: Helpers for TObject
« Reply #3 on: December 05, 2018, 02:45:53 pm »
More explanation:

AfterConstruction and BeforeDestruction are methods that are called Allways after and before a class by compiler. So i do not know how it behaves with a helper.

I would like to try something like this

Code: Pascal  [Select]
  1. {pseudocode}
  2.  
  3. Tobject.AfterConstruction;
  4. begin
  5.   {$IFDEF debugging}
  6.   BookkeepCreate (self); {add the Class to a list for debug info}
  7.   {$ENDIF}
  8. end;
  9.  
  10. Tobject.BeforeDestruction;
  11. begin
  12.    {$IFDEF debugging}
  13.    BookkeepDestroy (self); {bookeep the class as destroyed}  
  14.   {$ENDIF}
  15. end;
  16.  

Actually i have a class (xclass i called) that overrides these methods and all my classes descend for that class. It works fine. But there are a lot of Other's classes that descend from TObject that i cannot interpose xclass.

In debug mode i can from my bookeeper know if a Class was freed, have a debugging with the name of the classes and more things
« Last Edit: December 05, 2018, 03:08:18 pm by damieiro »

sash

  • Full Member
  • ***
  • Posts: 158
Re: Helpers for TObject
« Reply #4 on: December 05, 2018, 06:42:42 pm »
But there are a lot of Other's classes that descend from TObject that i cannot interpose xclass.

But who creates those "lot of Other's classes"? You, some kind of factory, or you need to log creation/destruction of every object in whole (yes, that's a lot) Application (what for)?
Lazarus 1.8.4 Unversioned directory FPC 3.0.4 x86_64-linux-gtk2 -- Ubuntu 18.04 XFCE

damieiro

  • Jr. Member
  • **
  • Posts: 61
Re: Helpers for TObject
« Reply #5 on: December 06, 2018, 07:46:15 pm »
Well i wish to log all creation/destruction of every object in whole.

For me, it's useful to account all, because if i (or other) misses freeing or reuse an already used object i can trace it and with useful debug info :). It's most a theoretical question. Nowadays, i will try it on compiler and see what happens :D


jamie

  • Hero Member
  • *****
  • Posts: 973
Re: Helpers for TObject
« Reply #6 on: December 06, 2018, 11:37:57 pm »
If the only interest is within laz applications then you can employ the "Stack Trace "  the back track and
you can see all the creates and destroys taking place.

damieiro

  • Jr. Member
  • **
  • Posts: 61
Re: Helpers for TObject
« Reply #7 on: December 07, 2018, 12:46:40 pm »
Well, yes...

But i prefer my own trace. And it gives me more options. For example, i can see the order of creation of the objects and deactivate with a single compiler option. I can in my xclass objects have fields with args to trace, and things like that.
The TObject trace is for completness and a teorical aproach. TObject misses fields i use on my own debugging info and it cannot be done with helpers.

damieiro

  • Jr. Member
  • **
  • Posts: 61
Re: Helpers for TObject
« Reply #8 on: December 07, 2018, 01:47:52 pm »
Well, i have tried a trashy code to see what happens;

I see that the helper hides (if not declared overloaded) the TObject.afterconstruction and TObject.BeforeDestruction, but TObject original method is allways called, not the helper one.

This code will put on console:

Build:TDescendant  -> Called By Tdescendant.AfterCreate;
Build:TDescendant2 -> Called By Tdescendan2.AfterCreate;

Hi World

Destroyed TObject:TDescendant  -> Called By Inherited,
Destroyed:TDescendant -> Called by TDescendant
Destroyed TObject:TDescendant2 -> called by inherited;
Destroyed:TDescendant2 -> Called by TDescendant from TDescendant2;

Code: Pascal  [Select]
  1. program tohelperstest;
  2.  
  3. uses classes, sysutils;
  4.  
  5. type
  6. TObjectHelper = class helper for TObject
  7.   public
  8.   procedure AfterConstruction; overload; {inherited wouldn't call it}
  9.   procedure BeforeDestruction; {overload; inherited would call it}
  10. end;
  11.  
  12.  
  13. procedure TObjectHelper.AfterConstruction;
  14. begin
  15.   writeln ('Buid TObject:',self.classname);
  16. end;
  17.  
  18. procedure TObjectHelper.BeforeDestruction;
  19. begin
  20.   writeln ('Destroyed TObject:',self.classname);
  21. end;
  22.  
  23.  
  24. type
  25. TDescendant= class (Tobject)
  26.   public
  27.   procedure AfterConstruction;override;
  28.   procedure BeforeDestruction;override;
  29. end;
  30.  
  31. procedure TDescendant.AfterConstruction;
  32. begin
  33.   inherited;  {which would call, helper or default? Answer: helper one if not overloaded}
  34.   writeln ('Build:',self.classname);
  35. end;
  36.  
  37. procedure TDescendant.BeforeDestruction;
  38. begin
  39.   inherited;
  40.   writeln ('Destroyed:',self.classname);
  41. end;
  42.  
  43. type
  44. TDescendant2= class (TDescendant)
  45.  
  46. end;
  47.  
  48.  
  49. var
  50.   MyClass: TObject;
  51.   MyDescendant:TDescendant;
  52.   MyDescendant2:TDescendant2;
  53.  
  54. begin
  55.   MyClass:=TObject.Create;
  56.   MyDescendant:=TDescendant.Create;
  57.   MyDescendant2:=TDescendant2.Create;
  58.  
  59.   writeln;
  60.   writeln ('Hi World');
  61.   writeln;
  62.  
  63.   FreeAndNil (MyClass);
  64.   FreeAndNil (MyDescendant);
  65.   FreeAndNil (MyDescendant2);
  66.   readln;
  67.  
  68. end.
  69.                                                                
  70.  

And well, the behaviour it's ok. The main doubt were what the compiler would call for a Tobject.BeforeDestruction ... If the original method, or the new method that hided the previous, or not.

The compiler will fire to the actual empty method. I do not know if this is the devs choice or not. I would prefer to fire to the Current visible method given by the helper, so it would give more meaning to the overload keyword, but i think all ways are ok.

« Last Edit: December 07, 2018, 02:01:57 pm by damieiro »

BrunoK

  • Jr. Member
  • **
  • Posts: 81
  • Retired programmer
Re: Helpers for TObject
« Reply #9 on: December 07, 2018, 02:27:05 pm »
May be you could try my modified heaptrc.pp, see attachment.

Objects creation/destruction can be tracked via a call back you can define.

Attachement readme.txt
Quote

readme.txt
----------

*** WARNING only tested on Windows in 32 bit mode with FPC 3.0.4 WARNING ***

What it does :
  - hooks (Patch) TObject.NewInstance and capture ClassType of created TObject and descendents.
  - on exiting, reports the class name and parenthood of the leaked object(s).
  Other features :   
    An optional custom text (ShortString) can be added at the beginning and/or end of the log.
    A callback can be defined to allow tracking of Creation/Destruction of objects.

HookHeap.7z contents :
readme.txt       this file   
heaptrc.pp      unit to copy to YourFPCFolder\rtl\inc
prjHookHeap.lpi      Test/demo project files
prjHookHeap.lpr
prjHookHeap_log.txt   Sample heaptrc log matching the test Project

This is free software, with no guaranty what so ever. MAKE SURE YOU HAVE A BACKUP.

To use the modified heaptrc.pp you need to rebuild your FPC.
Lazarus trunk 2018.11.07 (+/- patches regarding enabled, TScrollBar). FPC 3.0.4 32 bits. (+heaptrc with leaked ClassName+Revisited TList) , Windows 10 Pro x64 (v. 1803)

sash

  • Full Member
  • ***
  • Posts: 158
Re: Helpers for TObject
« Reply #10 on: December 07, 2018, 02:29:34 pm »
I see that the helper hides (if not declared overloaded) the TObject.afterconstruction and TObject.BeforeDestruction, but TObject original method is allways called, not the helper one.
...
I would prefer to fire to the Current visible method given by the helper, so it would give more meaning to the overload keyword.

It looks like you're ignoring comments and documentation.

It is very strange, your goal (???) is to trace proper memory allocation cycles for every object in application (rtl, fcl, lcl...), yet you didn't follow standard Pascal resource usage pattern (create-try-use-finally-free) in your (even test case) code.

No offense, but your task and efforts are complete mystery to me.
Lazarus 1.8.4 Unversioned directory FPC 3.0.4 x86_64-linux-gtk2 -- Ubuntu 18.04 XFCE

BrunoK

  • Jr. Member
  • **
  • Posts: 81
  • Retired programmer
Re: Helpers for TObject
« Reply #11 on: December 07, 2018, 02:41:12 pm »
Oh yes I forgot a sample of console output matching the demo/test with hook.

Code: Pascal  [Select]
  1. TObject NewInstance  Step=1
  2. TObject FreeInstance Step=1
  3. TObject NewInstance  Step=2
  4. TList NewInstance  Step=3
  5. TList FreeInstance Step=3
  6. TList NewInstance  Step=4
  7. TComponent NewInstance  Step=5
  8. TFPList NewInstance  Step=6
  9. Press ENTER to Quit
  10.  
Lazarus trunk 2018.11.07 (+/- patches regarding enabled, TScrollBar). FPC 3.0.4 32 bits. (+heaptrc with leaked ClassName+Revisited TList) , Windows 10 Pro x64 (v. 1803)

damieiro

  • Jr. Member
  • **
  • Posts: 61
Re: Helpers for TObject
« Reply #12 on: December 08, 2018, 11:52:06 pm »
BrunoK: I would see the code with time... But i'm trying doing it without use of internal compiler structures.

@sash
Quote
It looks like you're ignoring comments and documentation.

It is very strange, your goal (???) is to trace proper memory allocation cycles for every object in application (rtl, fcl, lcl...), yet you didn't follow standard Pascal resource usage pattern (create-try-use-finally-free) in your (even test case) code.

No offense, but your task and efforts are complete mystery to me.

No offense :). The try...finally boilerplate code i'm not using for testing behaviour because it could mask other things. And i'm trying very simple things, so i'm not going for the full template.

The documentation says what  you and i have posted, but documentation Do Not Say how a method Called by the Compiler like afterconstruction would be called if hidden. When i read the doc, i don't understand this would apply for these two so clearly. This method isn't called from a Class. So i have tested it. The method behaves like called from a method inside TObject, but as we know, TObject.create doesn't call it. So it's a special method with a special call and fired by the compiler to a fixed destination like calling it as self.afterconstruction...

As i said, it's a theoreticall work on how the compiler works. I seldom use LCL, for example, for graphics i usually go with SDL2.
On my own classes, i use a Tobject descendant to make all the traces and all my classes descend from it, and it has all the boilerplate needed :D




« Last Edit: December 09, 2018, 12:24:50 pm by damieiro »

sash

  • Full Member
  • ***
  • Posts: 158
Re: Helpers for TObject
« Reply #13 on: December 09, 2018, 12:01:46 pm »
documentation Do Not Say how a method Called by the Compiler like afterconstruction would be called if hidden.
...it's a theoreticall work

Well, some theory then  :D:
  0. Predecessor's code knows nothing about subsequent modifications and (as successor) you have no control on predecessor's code.
  1. Any modifications introduced to derived classes (either by inheritance or helpers) works only for derived classes.
  2. Introduced Helpers, when even allowed to access protected data of other's objects works only in newly introduced scope (see 0).
  3. A predecessor's method becomes "hidden" (as an effect of helpers or successor's reintroduced) only for instances of subsequent derived class (in newly introduced scope, see 0).

p.s. Although initial call of virtual method is placed in code by compiler, actually picked candidate is resolved at runtime (via vmt), not by compiler.
Lazarus 1.8.4 Unversioned directory FPC 3.0.4 x86_64-linux-gtk2 -- Ubuntu 18.04 XFCE

damieiro

  • Jr. Member
  • **
  • Posts: 61
Re: Helpers for TObject
« Reply #14 on: December 09, 2018, 12:36:19 pm »
Well, yes... These points are the theory. And i agree with them. I never use helpers for many considerations, but i'm trying to know compiler behaviour.

I think there are some more considerations. In this trashy code i insert a Tobject2.
It makes helpers to TObject (not TObject 2) in before and after construction.
These helpers seems ignored or simply i'm making an error in this fastly trashy code. For testing only i make one overload and other not. I cannot explain the behaviour well. I think at least TObject2 would call the helper code of TObject. But it  doesn't nothing... (like calling TObject main method and not being hidden by the helper) I think i'm making a mistake here (or bug)...

So, helper doesn't hide nothing for descendant. And it's ok for me (It should only hidde by proper hiding if i declare it on TObject2). But it doesn't match me well for theory. And i do not know how i could access the Helper from TObject2 without naming the helper explicitally.

Code: Pascal  [Select]
  1.  
  2. program helpers;
  3.  
  4. uses classes, sysutils;
  5.  
  6. type
  7.  
  8. TObject2=class
  9. constructor create;
  10. destructor destroy;override;
  11.  
  12. end;
  13.  
  14. constructor Tobject2.create;
  15. begin
  16.   {do things}
  17.   writeln ('tobject2 create init');
  18.   inherited afterconstruction; {do nothing}
  19.   self.afterconstruction;   {do nothing}
  20.   writeln ('tobject2 create end');
  21.  
  22.  
  23. end;
  24.  
  25. destructor Tobject2.destroy;
  26. begin
  27.   writeln ('tobject2 destructor init');
  28.  
  29.   inherited beforedestruction;
  30.   self.beforedestruction; {}
  31.   writeln ('tobject2 destructor end');
  32.   {do things}
  33. end;
  34.  
  35.  
  36. type
  37.  
  38. TObjectHelper = class helper for TObject
  39.   public
  40.   procedure AfterConstruction; overload;{inherited wouldn't call it}
  41.   procedure BeforeDestruction; {overload; inherited would call it}
  42. end;
  43.  
  44.  
  45. procedure TObjectHelper.AfterConstruction;
  46. begin
  47.   writeln ('Buid TObject:',self.classname);
  48. end;
  49.  
  50. procedure TObjectHelper.BeforeDestruction;
  51. begin
  52.   writeln ('Destroyed TObject:',self.classname);
  53. end;
  54.  
  55.  
  56. type
  57. TDescendant= class (Tobject2)
  58.   public
  59.   constructor create;
  60.   destructor destroy; override;
  61.  
  62.   procedure AfterConstruction;override;
  63.   procedure BeforeDestruction;override;
  64. end;
  65.  
  66. constructor TDescendant.create;
  67. begin
  68.  {no inherited to explain TObject create}
  69. end;
  70.  
  71. destructor TDescendant.destroy;
  72. begin
  73.   {same as above}
  74. end;
  75.  
  76.  
  77.  
  78. procedure TDescendant.AfterConstruction;
  79. begin
  80.  
  81.  writeln ('Build:',self.classname);
  82. end;
  83.  
  84. procedure TDescendant.BeforeDestruction;
  85. begin
  86.  
  87.   writeln ('Destroyed:',self.classname);
  88. end;
  89.  
  90. type
  91. TDescendant2= class (TDescendant)
  92.  
  93. end;
  94.  
  95.  
  96. var
  97.   MyClass: TObject2;
  98.   MyDescendant:TDescendant;
  99.   MyDescendant2:TDescendant2;
  100.  
  101. begin
  102.   MyClass:=TObject2.Create;
  103.   MyDescendant:=TDescendant.Create;
  104.   MyDescendant2:=TDescendant2.Create;
  105.  
  106.   writeln;
  107.   writeln ('Hi World');
  108.   writeln;
  109.  
  110.   FreeAndNil (MyClass);
  111.   FreeAndNil (MyDescendant);
  112.   FreeAndNil (MyDescendant2);
  113.   readln;
  114.  
  115. end.  
  116.  
« Last Edit: December 09, 2018, 12:41:59 pm by damieiro »