Recent

Author Topic: Reimplementing TObject  (Read 12888 times)

damieiro

  • Full Member
  • ***
  • Posts: 202
Reimplementing TObject
« on: August 24, 2021, 02:24:00 pm »
Hi!
As we know, Tobject is the main class for all.

TObject (big father) -> TPersistent (sons)-> TComponent -> TControl -> TGraphicalControl ... etc.

Well, i would like to add a new Method on TObject or reimplement .AfterConstruction or .BeforeDestruction.
How could it be done? Is it possible?

I have been doing some tests with a test Class that i called XClass with extended debug info if some compiler directives are set and works well. But i can only do it in my Xclass and its descendants. So code outside hierarchy cannot be tested.

And i don't want the memory debugs units like heaptrc, because doesn't fit my needs.

The simplest way for me would be to do in free pascal code the reimplementation but without touching the Original TObject (so the compiler will redirect these methods to the new ones like a deprecated method).

On the other hand (from the teoretically options how it could be done) Is it possible intercalate a new class  Like TObject -> TXclass -> TPersistent?. I think no for obvious reasons (Tpersitent would point to Tobject, but at least TXclass could override and add methods), and even it could be a bad decision (all new TObject descendants will not have the override)



MarkMLl

  • Hero Member
  • *****
  • Posts: 8515
Re: Reimplementing TObject
« Reply #1 on: August 24, 2021, 02:51:20 pm »
I wonder whether you could do what you want using a class helper? Note that (some versions of) the compiler insist on a singly-linked list of helpers, i.e. you can't have two helpers both "of" your base class but have to have "a helper of a helper".

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

BeniBela

  • Hero Member
  • *****
  • Posts: 948
    • homepage
Re: Reimplementing TObject
« Reply #2 on: August 24, 2021, 03:05:48 pm »
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode objfpc}{$H+}
  3. uses classes;
  4.  
  5. procedure MyAfterConstruction(self: TObject);
  6. begin
  7.    writeln('AfterConstruction: ', self.ClassName);
  8. end;
  9.  
  10. var o: TObject;
  11.   ovmt: PVmt;
  12.   p: TPersistent;
  13.   l: TList;
  14. begin
  15.   ovmt:=PVmt(TObject);
  16.   ovmt^.vAfterConstruction := @MyAfterConstruction;
  17.   ovmt:=PVmt(TPersistent);
  18.   ovmt^.vAfterConstruction := @MyAfterConstruction;
  19.   ovmt:=PVmt(TList);
  20.   ovmt^.vAfterConstruction := @MyAfterConstruction;
  21.   o := TObject.create;
  22.   p := TPersistent.Create;
  23.   l := TList.Create;
  24. end.              

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #3 on: August 24, 2021, 04:38:12 pm »
Thanks MarkMLI and BeniBela.

BeniBela shows a run-time redirection that would work for me, but with the con that it could be the first thing it should be made. And units/classes with initialization code could be creating objects before this could be executed. But it should work for my testing.

BeniBela: I would love that redirection would be done before any execution of other units (i would put that redirection in a "debug unit", so that "initialization" should be the first one. Is there a way or to force it some how? (for example, being the first in the uses clause on main program). <Edited: I see documentations and doubts in forum that it should be the first unit and not have nested units with initilization or will be the more nested initialization first).

AMLI: I haven't worked with helpers. I seems to add aditional method which is what i would like, but i do not know if i hide the virtual "AfterConstruction" how will be their behaviour.. I would do some testing :). On the other hand, it would be guaranteed it could be done before any run-time creation class by other initialization parts?
« Last Edit: August 24, 2021, 04:44:36 pm by damieiro »

MarkMLl

  • Hero Member
  • *****
  • Posts: 8515
Re: Reimplementing TObject
« Reply #4 on: August 24, 2021, 04:53:06 pm »
> guaranteed

I think that FPC is so top-heavy with gratuitous tricks that it's impossible to guarantee anything. However I'd suggest that familiarising yourself with the way that cmem and HeapTrc can be put right at the front of the main-program's import list might be useful.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 18524
  • Here stood a man who saw the Elbe and jumped it.
Re: Reimplementing TObject
« Reply #5 on: August 24, 2021, 05:05:42 pm »
Indeed.
Actually TObject is already overbloated.
If you need extensions, please derive and develop your own framework. Like everybody else does..... >:(
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #6 on: August 24, 2021, 07:47:26 pm »
Hi Thaddy

Indeed.
Actually TObject is already overbloated.
If you need extensions, please derive and develop your own framework. Like everybody else does..... >:(

I do not understand fully your answer.
In FPC all hangs from TObject. I can't change it. (is it possible to change it?).

But for debuggin Mess i prefer to catch an after construction object for accounting it. So in "my debug mode" i make a log with object class, order of execution, and then before it is destroyed, i make the bookeeping.
I can see if I forgot freeing things and if all its ok.

@MarkMLI: Same por memory heap operations, but these are much trikiest and it's needed some attention to avoid circular reference (your accounting should not do memory reserves or it will being in an endless loop). But for that thing i used other strategy (i make my own memory unit to make the calls, accounting in that).

The main issue for me is accounting objects with their info.

@Taddy: And i think TObject should be minimal, it was a teorical question how to add things to these, but i need also a tactical question for accounting objects that are derived from TObject  (all). The main field i would need to add for debuggin is some timestamp field or creation order..




skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Reimplementing TObject
« Reply #7 on: August 24, 2021, 10:19:16 pm »
On the other hand (from the teoretically options how it could be done) Is it possible intercalate a new class  Like TObject -> TXclass -> TPersistent?. I think no for obvious reasons (Tpersitent would point to Tobject, but at least TXclass could override and add methods), and even it could be a bad decision (all new TObject descendants will not have the override)
Yes and no!
If you want to "build in" TXClass before TPersistant what you need is to modify Classes unit. (the unit where TPersisent is declared)
And either explicitly specify TPresistant to inherit from TXClass
OR
add a unit to the uses clause of Classes: (and be careful as the it's OS specific. i.e. Win32 has it's own classes.pp, Linux has its own ... etc)
In the new unit you'd redeclare TObject type to point to TXClass.
Code: Pascal  [Select][+][-]
  1. unit XclassHijack;
  2.  
  3. inteface
  4.  
  5. uses xclassobj;
  6.  
  7. type
  8.   TObject = TXClass;
  9.  

Code: Pascal  [Select][+][-]
  1. unit Classes;
  2.  
  3. inteface
  4.  
  5. uses
  6.   rtlconsts,
  7.   sysutils,
  8.   types,
  9. {$ifdef FPC_TESTGENERICS}
  10.   fgl,
  11. {$endif}
  12.   typinfo,
  13.   windows
  14.   , XclassHijack;
  15.  
Next step is to recompile RTL. and this is something you might want or not want to do :)

Alternative ways (w/o recompiling RTL) is actually to modify VMT of TObject class in runtime.
So, instead of running default methods it would run your methods. Pretty much a hack.

However, if XClass also has some fields, it might not work. Accessing those fields will likely cause a crash.
« Last Edit: August 24, 2021, 10:22:07 pm by skalogryz »

MarkMLl

  • Hero Member
  • *****
  • Posts: 8515
Re: Reimplementing TObject
« Reply #8 on: August 24, 2021, 11:10:37 pm »
Alternative ways (w/o recompiling RTL) is actually to modify VMT of TObject class in runtime.
So, instead of running default methods it would run your methods. Pretty much a hack.

Would that work in all cases, or only where the method to be called was determined at runtime?

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #9 on: August 24, 2021, 11:14:17 pm »
well, skalogryz... thanks!

But my interest is to minimice all the havoc.
Tomorrow (if i can) i would redirect the TObject. It's a simple redirection.
Then i would try with helpers.
Helpers should add any "field" additions without too much trouble. But i do not know if a helper could give me a satisfactory and straigh forward redirection like "the hack"
And then, i will test the "hack" and a helper together.

As i said, the main idea is to make bookeeping of classes (creation, destruction, counts, timing...) if a "debug" unit is used and if it's not used, then nothing changes.

On the other hand i do not know if the redirection would be valid for all OSes. I think it would.

Well, thanks to all, i have one foot to start testings and you have saved me a lot of time.



damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #10 on: August 24, 2021, 11:44:03 pm »
@Benibela:

I tried the redirection, but it throws me a SIGSEGV on this line

Code: Pascal  [Select][+][-]
  1. ovmt^.vAfterConstruction := @MyAfterConstruction;   // throws SIGSEGV

damieiro

  • Full Member
  • ***
  • Posts: 202
Re: Reimplementing TObject
« Reply #11 on: August 25, 2021, 12:02:21 am »
@MarkMLI:

I tried helper, but it AfterContruction ignores the helper (seems to use the default). It only hides it as i see. And it cannot be overriden by sintax :O.

But i cannot undestand why benibela option doesn't work...

engkin

  • Hero Member
  • *****
  • Posts: 3112
Re: Reimplementing TObject
« Reply #12 on: August 25, 2021, 12:46:05 am »
I tried helper, but it AfterContruction ignores the helper (seems to use the default). It only hides it as i see. And it cannot be overriden by sintax :O.

Helpers will not help you here. (Sorry, couldn't help it)

Seriously, you should follow skalogryz's answer and recompile the RTL.


BeniBela

  • Hero Member
  • *****
  • Posts: 948
    • homepage
Re: Reimplementing TObject
« Reply #13 on: August 25, 2021, 03:00:23 am »
@Benibela:

I tried the redirection, but it throws me a SIGSEGV on this line

Code: Pascal  [Select][+][-]
  1. ovmt^.vAfterConstruction := @MyAfterConstruction;   // throws SIGSEGV

Weird

I tried it with FPC 3.2.2:



Code: Pascal  [Select][+][-]
  1. $  fpc
  2. Free Pascal Compiler version 3.2.2 [2021/05/21] for x86_64
  3.  
  4.  
  5. $  file  ~/tmp/project1
  6. project1: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), statically linked, not stripped
  7. $  ~/tmp/project1
  8. AfterConstruction: TObject
  9. AfterConstruction: TPersistent
  10. AfterConstruction: TList
  11.  
  12.  
  13. $  file  ~/tmp/project1.exe
  14. project1.exe: PE32 executable (console) Intel 80386, for MS Windows
  15. $  wine ~/tmp/project1.exe
  16. An unhandled exception occurred at $00401757:
  17. EAccessViolation: Access violation
  18.   $00401757  main,  line 16 of project1.lpr
  19.  
  20.  
  21.  

skalogryz

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 2770
    • havefunsoft.com
Re: Reimplementing TObject
« Reply #14 on: August 25, 2021, 06:42:43 am »
But i cannot undestand why benibela option doesn't work...
This is likely because the code is put into ".text" section of the executable.

Such code is marked as "READONLY", meaning when the executable is loaded (and the process is started) the memory area is marked as readonly and cannot be modified.
This quite a handy feature as prevents the code from being modified by accident (i.e. due to some bad code).
Otherwise some errors might turn debugging into hell, since they would be modifying the existing code.

An attempt to modify VMT is the exact example of an attempt to change READONLY memory.


The attribute of readonly is assigned by the linker that's generating the executble. I don't think that the FPC internal linker allows to use a different attribute. But using the external linker (ld) it might be possible
« Last Edit: August 25, 2021, 07:59:02 am by skalogryz »

 

TinyPortal © 2005-2018