Recent

Author Topic: Really VOID Class  (Read 11797 times)

damieiro

  • Full Member
  • ***
  • Posts: 200
Really VOID Class
« on: September 23, 2021, 07:35:20 pm »
In the class model of Free Pascal, derived from Delphi one, we have
TObject.  -> https://wiki.lazarus.freepascal.org/TObject
And all derives from it.

I think it's desirable some kind of lightweight class without all the bloat like a
TVoidClass and TObject should derive from the TVoidClass.
TVoidClass shoud be really void (not constructor, destructor, anything), or by default a minimal set.
Even things could have more steps

TVoidClass (a really void one) -> THeapClass (with default constructor and destructor on heap) -> Tobject

This would give full delphi/Embarcadero compliance and could be the way to have a full control on the class.

Embedded systems will make use of these instead of not developed stack object model and when some improvements were doing on the classes, it will be of use for void ones...


*TVoidClass,THeapClass are example names... not the coolest ones :D

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Really VOID Class
« Reply #1 on: September 23, 2021, 07:41:46 pm »
Moreover what they are exactly good for is unclear  too.

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Really VOID Class
« Reply #2 on: September 23, 2021, 07:53:46 pm »
Well, i think is good for some reasons:

1.- Less memory use. A void class has minimal memory footprint.

2.- Less side-effects. For example if you don't define an AfterConstruction it will not be called as TObject does. If not a create constructor defined, it cannot be invoked for a descendant class, etc..

3.- Not  the main idea of this, but if the model evolves, a void class could help to derive on how to make the memory allocation, and being a stack or heap or other method of allocation being a pure implementation of their descendant classes. For example a Vector class or Sprite class, could use a GPU memory model.

4.- For actually implementation, a TObject would be a descendant from a void.

5.- Are we really using all the data of TObject in our code? I think not.

6.- Could help C++ portability and viceversa. C++uses a "semivoid" . Even a TCpp class could descend from a Void one.

7.- I think on a void class as a tool, not as a trouble ;)

I think in a lot of posibilites, but all are derived from a basic void class..

Awkward

  • Full Member
  • ***
  • Posts: 134
Re: Really VOID Class
« Reply #3 on: September 23, 2021, 08:14:25 pm »
maybe easier to use standard 'object' type then?

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Really VOID Class
« Reply #4 on: September 23, 2021, 08:28:35 pm »
Object would be ok, but It's not valid idea, because.

1.- Object is a stack type one, not heap. (can be heap, but without sugar sintax)
2.- Object is deprecated in delphi and only maintained in fpc, no further development
3.- Object doesn't accept all the syntax and has no sugar-syntax implemented.

The dual model Object/class i think it's a bad idea. And heap classes has it's own advantages.

Even with object, it should be a void Class for completness (and the explained advantages)

« Last Edit: September 23, 2021, 08:30:13 pm by damieiro »

Awkward

  • Full Member
  • ***
  • Posts: 134
Re: Really VOID Class
« Reply #5 on: September 23, 2021, 08:52:18 pm »
dual model is bad idea? say that to advanced record creators! THAT was a bad idea, to make records with bonuses if we has objects already. And your Void classes will be little more than unuseful coz direct descendants will be not compatible with TObject descendants

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Really VOID Class
« Reply #6 on: September 23, 2021, 10:08:36 pm »
Quote
dual model is bad idea? say that to advanced record creators! THAT was a bad idea, to make records with bonuses if we has objects already.

Dual model without full support *IS* bad idea. Yes. Better a full supported one.
And a stack object isn't a heap class. Well it is with manual management like any type. But well, a void class could be (with some tweaking) be the same as a object and interchangeable in pointers...

 
Quote
And your Void classes will be little more than unuseful coz direct descendants will be not compatible with TObject descendants
Which is intended. Do you need a Tobject descendand?. Declare a TMyClass=class(TObject).
Do you need a Void class descendant? Declare TMyClass=class(Void) or TMyClass=voidclass because TMyClass=class, it's taken as class(TObject).. so we can't use it.

But i do not need that over defined and oversized TObject. So give me the raw object to interact with this.

When you declare a Tcar=class... Do you use...
...DefaultHandler()?
...InstanceSize?
...GetInterface()?.
for example.
would like that your new class should not answer to .create or .destroy? (not fail safe)

This TObject in all his glory

Code: Pascal  [Select][+][-]
  1.    TObject = class
  2.        public
  3.           { please don't change the order of virtual methods, because
  4.             their vmt offsets are used by some assembler code which uses
  5.             hard coded addresses      (FK)                                 }
  6.           constructor Create;
  7.           { the virtual procedures must be in THAT order }
  8.           destructor Destroy;virtual;
  9.           class function newinstance : tobject;virtual;
  10.           procedure FreeInstance;virtual;
  11.           function SafeCallException(exceptobject : tobject;
  12.             exceptaddr : codepointer) : HResult;virtual;
  13.           procedure DefaultHandler(var message);virtual;
  14.  
  15.           procedure Free;
  16.           class function InitInstance(instance : pointer) : tobject; {$ifdef SYSTEMINLINE} inline; {$endif}
  17.           procedure CleanupInstance;
  18.           class function ClassType : tclass;{$ifdef SYSTEMINLINE}inline;{$endif}
  19.           class function ClassInfo : pointer;
  20.           class function ClassName : shortstring;
  21.           class function ClassNameIs(const name : string) : boolean;
  22.           class function ClassParent : tclass;{$ifdef SYSTEMINLINE}inline;{$endif}
  23.           class function InstanceSize : SizeInt;{$ifdef SYSTEMINLINE}inline;{$endif}
  24.           class function InheritsFrom(aclass : tclass) : boolean;
  25.           class function StringMessageTable : pstringmessagetable;
  26.  
  27.           class function MethodAddress(const name : shortstring) : codepointer;
  28.           class function MethodName(address : codepointer) : shortstring;
  29.           function FieldAddress(const name : shortstring) : pointer;
  30.  
  31.           { new since Delphi 4 }
  32.           procedure AfterConstruction;virtual;
  33.           procedure BeforeDestruction;virtual;
  34.  
  35.           { new for gtk, default handler for text based messages }
  36.           procedure DefaultHandlerStr(var message);virtual;
  37.  
  38.           { message handling routines }
  39.           procedure Dispatch(var message);virtual;
  40.           procedure DispatchStr(var message);virtual;
  41.  
  42.           { interface functions }
  43.           function GetInterface(const iid : tguid; out obj) : boolean;
  44.           function GetInterface(const iidstr : shortstring;out obj) : boolean;
  45.           function GetInterfaceByStr(const iidstr : shortstring; out obj) : boolean;
  46.           function GetInterfaceWeak(const iid : tguid; out obj) : boolean; // equal to GetInterface but the interface returned is not referenced
  47.           class function GetInterfaceEntry(const iid : tguid) : pinterfaceentry;
  48.           class function GetInterfaceEntryByStr(const iidstr : shortstring) : pinterfaceentry;
  49.           class function GetInterfaceTable : pinterfacetable;
  50.  
  51.           { new since Delphi 2009 }
  52.           class function UnitName : {$ifdef FPC_HAS_FEATURE_ANSISTRINGS}ansistring{$else FPC_HAS_FEATURE_ANSISTRINGS}shortstring{$endif FPC_HAS_FEATURE_ANSISTRINGS};
  53.           class function QualifiedClassName: {$ifdef FPC_HAS_FEATURE_ANSISTRINGS}ansistring{$else FPC_HAS_FEATURE_ANSISTRINGS}shortstring{$endif FPC_HAS_FEATURE_ANSISTRINGS};
  54.           function Equals(Obj: TObject) : boolean;virtual;
  55.           function GetHashCode: PtrInt;virtual;
  56.           function ToString: {$ifdef FPC_HAS_FEATURE_ANSISTRINGS}ansistring{$else FPC_HAS_FEATURE_ANSISTRINGS}shortstring{$endif FPC_HAS_FEATURE_ANSISTRINGS};virtual;
  57.        end;                      
  58.  
  59. end;


TObject is half bloated, half hacky and full oversized for nearly 99.9% of uses.
Note: this is not a dev blame. It's being as this for historical reasons, but a raw heap class is, for me, a desirable one.
« Last Edit: September 23, 2021, 10:38:26 pm by damieiro »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Really VOID Class
« Reply #7 on: September 24, 2021, 09:16:56 am »
I think it's desirable some kind of lightweight class without all the bloat like a
TVoidClass and TObject should derive from the TVoidClass.

There is no desire to change or extend the object model. If you need something lightweight use object.

TObject is half bloated, half hacky and full oversized for nearly 99.9% of uses.

First: if smartlinking is enabled (default on Windows) any non virtual methods of TObject are removed.
Second: nothing there is hacky, it's simply a type that makes use of the low level information generated by the compiler, that's why it's a core type of the RTL.
Third: It's not oversized for 99.9% of uses, because the LCL makes use of nearly all the functionality provided by TObject to provide all the functionality we know and love and LCL usage is clearly more than 0.1%.

alpine

  • Hero Member
  • *****
  • Posts: 1038
Re: Really VOID Class
« Reply #8 on: September 24, 2021, 10:41:05 am »
IMHO advanced records aren't a bad idea. See https://forum.lazarus.freepascal.org/index.php/topic,46306.msg329970.html#msg329970

Such auto objects gives some advantages that C++ has by default.
"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Really VOID Class
« Reply #9 on: September 24, 2021, 12:33:18 pm »
Quote
I think it's desirable some kind of lightweight class without all the bloat like a
TVoidClass and TObject should derive from the TVoidClass.

There is no desire to change or extend the object model. If you need something lightweight use object.


It's not the same and you know it. Object won't be developed further. It has not the sugar candy syntax. It's stacked, not in heap.. Same you said that it's a bad idea being with objects because their limitations can't be offered as a similar solution. It's not the same and it hasn't the same priority. And are useless in Delphi (embarcadero deprecated them).
And it's a bad idea two types of coding (for objects) and for TObjects. Duality for same things is allways bad idea. Telling that use object instead of making a voidheap one is a straw man argument. https://en.wikipedia.org/wiki/Straw_man
This argument will be acceptable if object would hace exactly the same functionality, nor less as a heap object and being sugar-sintax in heap and fully mainteinance. Object its even an antecesor of record with methods. It's an ancient way of doing things and not a supported one. Would you advise to ANY who is starting with fpc to use objects or classes? You will answer: go for classes. So no room for : Go for objects as an advice.

Quote
TObject is half bloated, half hacky and full oversized for nearly 99.9% of uses.

First: if smartlinking is enabled (default on Windows) any non virtual methods of TObject are removed.
Second: nothing there is hacky, it's simply a type that makes use of the low level information generated by the compiler, that's why it's a core type of the RTL.
Third: It's not oversized for 99.9% of uses, because the LCL makes use of nearly all the functionality provided by TObject to provide all the functionality we know and love and LCL usage is clearly more than 0.1%.

First, smartlinking only removes interface ones (below). The other are class functions that could viewed as compiler constructs and being out of class scope or in the class scope (but this is taste) -> This is RTL not a part of a class. And should be the class designer if use it or other compiler info. Give TObject as is, and let a void one to be generated as object with the same funcionality as object and fully maintained. And it's how an academic OOP should be. Why is this change problematic?. It's making the actual model to an universal, fully academic supported and fully compatible with existing code. This can have many views. One can have (by sugar sintax) define that class-RTL methods could be in all classes (like in a void class)-> example
(MyAscendant:=TFooClass.ClassParent) or as (MyAscendant:=ClassParent (TFooClass); And i could argue the later can have advantages too. could you do this with an object. NO.
But the main question is why is difficult or not viewed as neccesary a Void object which should be the academic root to start working and gives tools to do other things and not Lazarus-delphi ones? I think it's a autoimposed limitation.

Second, better not doing a smartlinking job when you don't need to smart link if better defined.


Not too much gain...
TObject = class
       public
          { please don't change the order of virtual methods, because
            their vmt offsets are used by some assembler code which uses
            hard coded addresses      (FK)                                 }
          constructor Create;
          { the virtual procedures must be in THAT order }
          destructor Destroy;virtual;
          class function newinstance : tobject;virtual;
          procedure FreeInstance;virtual;
          function SafeCallException(exceptobject : tobject;
            exceptaddr : codepointer) : HResult;virtual;
          procedure DefaultHandler(var message);virtual;
 
          procedure Free;
          class function InitInstance(instance : pointer) : tobject; {$ifdef SYSTEMINLINE} inline; {$endif}
          procedure CleanupInstance;
          class function ClassType : tclass;{$ifdef SYSTEMINLINE}inline;{$endif}
          class function ClassInfo : pointer;
          class function ClassName : shortstring;
          class function ClassNameIs(const name : string) : boolean;
          class function ClassParent : tclass;{$ifdef SYSTEMINLINE}inline;{$endif}
          class function InstanceSize : SizeInt;{$ifdef SYSTEMINLINE}inline;{$endif}
          class function InheritsFrom(aclass : tclass) : boolean;
          class function StringMessageTable : pstringmessagetable;
 
          class function MethodAddress(const name : shortstring) : codepointer;
          class function MethodName(address : codepointer) : shortstring;
          function FieldAddress(const name : shortstring) : pointer;
 
          { new since Delphi 4 }
          procedure AfterConstruction;virtual;
          procedure BeforeDestruction;virtual;
 
          { new for gtk, default handler for text based messages }
          procedure DefaultHandlerStr(var message);virtual;
 
          { message handling routines }
          procedure Dispatch(var message);virtual;
          procedure DispatchStr(var message);virtual;
 
          { interface functions }
          function GetInterface(const iid : tguid; out obj) : boolean;
          function GetInterface(const iidstr : shortstring;out obj) : boolean;
          function GetInterfaceByStr(const iidstr : shortstring; out obj) : boolean;
          function GetInterfaceWeak(const iid : tguid; out obj) : boolean; // equal to GetInterface but the interface returned is not referenced
          class function GetInterfaceEntry(const iid : tguid) : pinterfaceentry;
          class function GetInterfaceEntryByStr(const iidstr : shortstring) : pinterfaceentry;
          class function GetInterfaceTable : pinterfacetable;

 
          { new since Delphi 2009 }
          class function UnitName : {$ifdef FPC_HAS_FEATURE_ANSISTRINGS}ansistring{$else FPC_HAS_FEATURE_ANSISTRINGS}shortstring{$endif FPC_HAS_FEATURE_ANSISTRINGS};
          class function QualifiedClassName: {$ifdef FPC_HAS_FEATURE_ANSISTRINGS}ansistring{$else FPC_HAS_FEATURE_ANSISTRINGS}shortstring{$endif FPC_HAS_FEATURE_ANSISTRINGS};
          function Equals(Obj: TObject) : boolean;virtual;
          function GetHashCode: PtrInt;virtual;
          function ToString: {$ifdef FPC_HAS_FEATURE_ANSISTRINGS}ansistring{$else FPC_HAS_FEATURE_ANSISTRINGS}shortstring{$endif FPC_HAS_FEATURE_ANSISTRINGS};virtual;
       end;                     
 
end;



« Last Edit: September 24, 2021, 07:51:20 pm by damieiro »

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Really VOID Class
« Reply #10 on: September 24, 2021, 12:47:48 pm »
1.- Object is a stack type one, not heap. (can be heap, but without sugar sintax)
2.- Object is deprecated in delphi and only maintained in fpc, no further development
3.- Object doesn't accept all the syntax and has no sugar-syntax implemented.
1. So is C++ did you know that?
2. There are reports at Embarcadero to deprecate deprected for objects
3. True, but still much of it

If you are reluctant to use objects you can always use class abstract.
On small, memory pressured, embedded platforms I would go for either Object or fully procedural code.
Specialize a type, not a var.

damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Really VOID Class
« Reply #11 on: September 24, 2021, 12:48:32 pm »
IMHO advanced records aren't a bad idea. See https://forum.lazarus.freepascal.org/index.php/topic,46306.msg329970.html#msg329970

Such auto objects gives some advantages that C++ has by default.

Advanced records it's a good idea. It's even a modern idea. And perhaps (i do not know) better implemented in FPC that object.

But advanced records has other target. These goes for encapsulation, but not for inheritance.
And advanced records use the same memory schema and syntax for all.

But classes and object were two foundryes for same target: A OOP model. C++ has ONE model for ONE target. We have TWO models (one being deprecated slowly) and other with a healthy future.
But TObject is not Zero-Starting Object as their stack counterpart.
And for that reason one cannot do auto objects or other way of doing things. Even their method like AfterConstructions cannot be intercepted for debug (which is really weird).
But the worse is that i think that argumentation (with more or less academical aproach or things like that) is, for me, a bit dissapointing.
For example, the good reasons for having a void object class or intercept/callback a clearly debug-targeted method (as after  construction) will be more "operable" if embarcadero does their own interception. Then we go for support it. If not, it won't be. And i would like to thing more on which is convenient to have and will give a better foundation for future.

It's like the operator mess options: A bad idea if you have a really equivalent way to do it. And a good idea if this could give useful hint to compiler. Not a pascal/c syntax flame. Simply put it what gives to you.
A void object gives you a raw object with all the power. And with the advantage that are a good existing foundry to mimic like TObject. And with nearly Zero Cost.

My view /INMHO:

Advanced records: It's ok. Targetting for encapsulation, not inheritance.  Has its room

Object/TObject: Two things to target the same, first deprecated, the second less academical by autoimposing limits, but far better developed and supported. So the future of a void OOP start is with TObject. There is not other way to aproach actually.


damieiro

  • Full Member
  • ***
  • Posts: 200
Re: Really VOID Class
« Reply #12 on: September 24, 2021, 12:55:38 pm »
Quote
    1.- Object is a stack type one, not heap. (can be heap, but without sugar sintax)
    2.- Object is deprecated in delphi and only maintained in fpc, no further development
    3.- Object doesn't accept all the syntax and has no sugar-syntax implemented.
Quote
1. So is C++ did you know that?
2. There are reports at Embarcadero to deprecate deprected for objects
3. True, but still much of it

If you are reluctant to use objects you can always use class abstract.
On small, memory prssured, embedded applications I would go for either Object or fully procedural code.

1.- This is C++ basis. Do you know that i know that? -> More than 30 years with pascal/c/c++. I started with Z80/8088 assembly... And here are people with far more experience. We are all veterans if we are discussing a teoretical model and their deep consecuences.. Please, do not treat others as beginners. And for the sake of the argument. Yes this is a C++ model. A ancient one and a robust one. What are wrong if we can do C++ things in a pascal syntax?. Is it some kind of grial not taking any idea that can be useful if not pascal existant (which is not true: really we are with Delphi-class)?
We can say that TP-Object is c++ too. Or we can agree that there is a academic model and implementations. And a void/root object is nearer to the teoretical. And not only c does it. Pascal does it with TP-Object but it won't give their extension and borland were for the class one..
2.- Embarcadero is not my way. You shouldn't use a languaje construct that it's not safe their continuity or depends on a private company. That was the Borland story.. Want we to repeat same mistake?
3.- But still bad supported and i think we are making a zombi. Let object dead (or deprecate gently) and then we can go for a Class complete model that should allow lightweight things or tinkering. And remove all dual mainteinance of compiler. With more tools we can construct even more tools. If you only give me a plastic hammer for not harming me, i won't do the job.

Quote
If you are reluctant to use objects you can always use class abstract.
And it will be marvellous if i can declare TMyAbstractClass = class (ReallyEmptyVoidClass) and then define it and it only occupies their vmts and tables with the minimal operational info. And less memory movement.. and better maintenance than procedural-only.

Quote
On small, memory pressured, embedded platforms I would go for either Object or fully procedural code.
Well, in this scenario i go for only procedural (better say:structured) paradigm and not mix with OOP paradigm in Pascal because i couldn't rely on their future. If a void class or similar c++ construct (which should be obvious to put on top of any hierarchy like object) i would prefer oop if the memory pressure is not very very critical. This applies to C++  too.



« Last Edit: September 24, 2021, 07:56:20 pm by damieiro »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Really VOID Class
« Reply #13 on: September 24, 2021, 01:32:44 pm »
But the main question is why is difficult or not viewed as neccesary a Void object which should be the academic root to start working and gives tools to do other things and not Lazarus-delphi ones?

Simply because it's not necessary. If you need something lightweight you can use records or objects. There is simply no need to introduce yet another concept.

Not too much gain...

Even then the methods exist only once inside the binary. Yes, the virtual methods are referenced in each VMT, but each VMT only exists once per class type. There are much bigger size related problems than that in the RTL (e.g. that the whole managers for strings, memory, threads, etc. can't be smartlinked).

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Really VOID Class
« Reply #14 on: September 24, 2021, 02:04:25 pm »
On small, memory pressured, embedded platforms I would go for either Object or fully procedural code.

Let's face it, conventional wisdom has it that the entire concept of a heap is incompatible with resource-constrained systems. OK so you can deallocate locally in the reverse order to allocation... but at that point you might as well be putting stuff on the stack. Except of course that the stack has its own hazards if the platform can't increase its size on demand. Which is something that the majority of CS graduates find hard to stomach, which makes them fundamentally unsuited to embedded system work.

Elsewhere this morning I spotted this:

Quote
Meanwhile the DOS version uses .NET Core’s ability to produce self-contained executables along with some very significant tricks to pare down the size of the finished program from many megabytes to an eventual DOS-suitable 27k.
https://hackaday.com/2020/02/09/c-the-language-for-all-platforms-now-including-windows-3-11-and-dos/

Allowing that that was probably a minimal "Hello, World!" it would still be vastly excessive on a classical embedded system. But it does indicate the extent to which at least some modern tools developers treat ideas of economy and efficiency with contempt... all in the name of "accelerating the development cycle" which ultimately means that the consumer can watch his cat videos with a different background colour.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

 

TinyPortal © 2005-2018