Recent

Author Topic: "class" without inheritance (Solved)  (Read 3355 times)

beria

  • Jr. Member
  • **
  • Posts: 70
"class" without inheritance (Solved)
« on: September 14, 2022, 11:02:51 am »
Is it possible to long create a variable of type "class" without ancestors, so that it itself is the root abstract class for the descendants...  The description explicitly forbids this, but maybe there are ways around it? Everything can be done through "object", but I have a feeling they will soon be cut out of the language since no one uses them as obsolete. As I see "record" unfortunately doesn't have any inheritance methods and therefore can't make universal call for any dataset, and "generics" is more like complicated compiler macros which do a lot of doubling of almost the same code, but still don't solve the problem because different sets of variable types usually have different ways of their processing and input/output...

Translated with www.DeepL.com/Translator (free version)
« Last Edit: September 14, 2022, 10:50:56 pm by beria »

egsuh

  • Hero Member
  • *****
  • Posts: 1291
Re: "class" without inheritance
« Reply #1 on: September 14, 2022, 11:10:49 am »
If I understand correctly, why NOT? It's what "class" intends to do...   
All classes are descendant of TObject, but it is not visible.
« Last Edit: September 14, 2022, 11:23:37 am by egsuh »

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: "class" without inheritance
« Reply #2 on: September 14, 2022, 11:18:39 am »
It - super lightweight old school objects - won't be cut out of the language any time soon.
And TObject is already the root object. It is currently very light weight, although that might change.

Maybe you can explain what you really want to achieve? For some scenarios there are some options.
For example, although the constructor for TObject is non-virtual, so it basically always called, it is possible to hide it, so it is never called. I posted on this forum about how to do that some years ago.
« Last Edit: September 14, 2022, 11:36:26 am by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: "class" without inheritance
« Reply #3 on: September 14, 2022, 12:01:05 pm »
Somebody prepared this document a few months ago looking at the difference between classes, objects, advanced records etc., which I think is worth reading.

https://forum.lazarus.freepascal.org/index.php?action=dlattach;topic=57040.0;attach=46085

https://github.com/zsoltszakaly/OOPstructuresinpascal/blob/main/structures%20in%20pascal.odt

I think that the .odt version is slightly newer, and the author is interested in any clarifications that the community can provide.

MarkMLl
« Last Edit: September 14, 2022, 12:13:05 pm by 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

beria

  • Jr. Member
  • **
  • Posts: 70
Re: "class" without inheritance
« Reply #4 on: September 14, 2022, 12:17:19 pm »
It - super lightweight old school objects - won't be cut out of the language any time soon.
And TObject is already the root object. It is currently very light weight, although that might change.

Maybe you can explain what you really want to achieve? For some scenarios there are some options.
For example, although the constructor for TObject is non-virtual, so it basically always called, it is possible to hide it, so it is never called. I posted on this forum about how to do that some years ago.

A bidirectional list of arbitrary elements.
But not through dynamic arrays, as usual, but with classic algorithm with fast insertion and deletion, which arrays don't provide and at many thousands of elements they start to slow down, so I had to give them up...

The standard algorithm, as per the textbook, works very well (even amazingly) in terms of speed and uses "record" types.
It's like this.
Code: Pascal  [Select][+][-]
  1.    PBDIthem = ^TBDIthem;
  2.   TBDIthem = record
  3.     Data: byte; // Anything here
  4.     Prev, Next: PBDIthem;
  5.     Create procedure;
  6.     procedure Done;
  7.   end;
  8.  
  9.   TBDList = record
  10.   public
  11.     FirstPtr: PBDIthem;
  12.     EndPtr: PBDIthem;
  13.     CurrentList: PBDIthem;
  14.     Len: nativeuint;
  15.     Current: nativeuint;
  16.     Create procedure;
  17.     Done procedure;
  18.     procedure IncCurrent(Count: nativeuint);
  19.   -*-*-*-*-*-*-*-*-*-*



But since the list in the Data field can contain anything, including dynamic variables that must be deleted later, it is not suitable and we need inheritance.
So, it's either "class" which, because of TClass always draws unbelievable amount of trash into each list item in the form of completely unnecessary inherited table of huge number of virtual methods.   Or the old "object" type as I understand, deprived of all this and even Lazarus gives me in the hint only those fields and methods, which I defined myself....

" I posted on this forum about how to do that some years ago." -  Can you give me a link to read...





MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: "class" without inheritance
« Reply #5 on: September 14, 2022, 12:27:11 pm »
But since the list in the Data field can contain anything, including dynamic variables that must be deleted later, it is not suitable and we need inheritance.

Why? An instance of a class is just a pointer to the something on the heap, so there's nothing to stop you creating a linked list of records using classic techniques with each referring to an instance. When consuming, you apply the "is" operator to each to work out what it is.

I'd add however that if you increase the size of a dynamic array in chunks you'll find it vastly more efficient than doing it element-by-element.

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

beria

  • Jr. Member
  • **
  • Posts: 70
Re: "class" without inheritance
« Reply #6 on: September 14, 2022, 01:06:23 pm »
But since the list in the Data field can contain anything, including dynamic variables that must be deleted later, it is not suitable and we need inheritance.

Why? An instance of a class is just a pointer to the something on the heap, so there's nothing to stop you creating a linked list of records using classic techniques with each referring to an instance. When consuming, you apply the "is" operator to each to work out what it is.

I'd add however that if you increase the size of a dynamic array in chunks you'll find it vastly more efficient than doing it element-by-element.

MarkMLl
1. Even if I increase the size of the array by 100000 elements at once, it leads to the fact that at some point the whole program hangs for a few seconds (on a netbook at all for 1.5 minutes and with some probability crashes due to lack of memory).  With the list, as I tested, including copying lists into each other up to a million entries, there are no such problems at all and much more compactly uses memory, even taking into account the cost of extra bytes for two reference variables.
Well, if I need sorting, here - then also much better in terms of performance "AVL Tree"


2.Doesn't "is" only work with classes and interfaces?


MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: "class" without inheritance
« Reply #7 on: September 14, 2022, 01:11:34 pm »
1. Even if I increase the size of the array by 100000 elements at once, it leads to the fact that at some point the whole program hangs for a few seconds (on a netbook at all for 1.5 minutes and with some probability crashes due to lack of memory).  With the list, as I tested, including copying lists into each other up to a million entries, there are no such problems at all and much more compactly uses memory, even taking into account the cost of extra bytes for two reference variables.
Well, if I need sorting, here - then also much better in terms of performance "AVL Tree"

Fair comment :-)

Quote
2.Doesn't "is" only work with classes and interfaces?

It applies to instances of classes, and I specifically said that you could put (a pointer to) an instance (which itself is on the heap) as the payload in your list.

Note that I am carefully avoiding referring to objects here, because of the confusion described adequately in the document I cited (rough translation: eating my lunch is more interesting than typing :-)

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

PascalDragon

  • Hero Member
  • *****
  • Posts: 5481
  • Compiler Developer
Re: "class" without inheritance
« Reply #8 on: September 14, 2022, 01:13:38 pm »
Is it possible to long create a variable of type "class" without ancestors, so that it itself is the root abstract class for the descendants...  The description explicitly forbids this, but maybe there are ways around it?

No. Every class implicitly inherits from TObject. There is no way around this.

Everything can be done through "object", but I have a feeling they will soon be cut out of the language since no one uses them as obsolete.

TP-style objects are still used by many people (not to mention the FreeVision framework which is used by the textmode IDE) and they're also required for compatibility to TP so there is absolutely no reason to remove them.

BrunoK

  • Sr. Member
  • ****
  • Posts: 452
  • Retired programmer
Re: "class" without inheritance
« Reply #9 on: September 14, 2022, 05:20:36 pm »
So, it's either "class" which, because of TClass always draws unbelievable amount of trash into each list item in the form of completely unnecessary inherited table of huge number of virtual methods.
Ridiculous.
Code: Pascal  [Select][+][-]
  1.   TBDItem = class
  2.     AddSeq: integer; // Anything here
  3.     Prev, Next: TBDItem;
  4.     procedure UnlinkAndFree(aBDList: TBDList);
  5.   end;
One instance of each of these objects requires 32 bytes of memory on x86_64
That is : 8 for the object ClassType
             4 for integer
           16 for and Prev, Next
Total    28 rounded up to next nearest 8 bytes boundary (SizeOf(Pointer)) = 32 bytes

In 32 bit mode 1 instance requires 4 + 4 + 8 = 16 bytes and that is a multiple of SizeOf(Pointer)

To know the size of the constant part of a TObject descendent do
  WriteLn(TBDItem.InstanceSize);

You can add as many methods to the class, it wont increase the data storage of an instance. Here UnlinkAndFree does not require any memory for additional TBDItem instance.

Defined as a record it might take 1 SizeOf(pointer) less bytes, but New(PBDIthem) record is probably going to be also upsized by the memory manager to 4 or 8 byte boundary.



beria

  • Jr. Member
  • **
  • Posts: 70
Re: "class" without inheritance
« Reply #10 on: September 14, 2022, 11:03:49 pm »
So, it's either "class" which, because of TClass always draws unbelievable amount of trash into each list item in the form of completely unnecessary inherited table of huge number of virtual methods.
Ridiculous.

see the minimum VMT size for each class in objpash.inc, in the assembler text, or in  https://www.freepascal.org/docs-html/current/prog/progsu166.html#x210-2240008.2.13
And this is only references and without a lot of dolnitel character information, like class name...
Is it really necessary, for a bunch of tiny classes encapsulating only one byte, for example?

beria

  • Jr. Member
  • **
  • Posts: 70
Re: "class" without inheritance
« Reply #11 on: September 14, 2022, 11:10:12 pm »
]

It applies to instances of classes, and I specifically said that you could put (a pointer to) an instance (which itself is on the heap) as the payload in your list.

Note that I am carefully avoiding referring to objects here, because of the confusion described adequately in the document I cited (rough translation: eating my lunch is more interesting than typing :-)

MarkMLl

The question of compactness, versatility and speed has been resolved in an extremely radical way... Through helpers.....
The code is still very unfinished, with a minimum of functionality and not optimized, but it works...

Code: Pascal  [Select][+][-]
  1. TPointerHelper = type helper for Pointer
  2.     constructor createBuffer(AValue: nativeuint);
  3.     constructor createLongInt(AValue: longint);
  4.     constructor createShortString(AValue: shortstring);
  5.     procedure InitPrev(AValue: Pointer); inline;
  6.     function GetPrev: Pointer; inline;
  7.     procedure GetLongint(var AValue: longint);
  8.     procedure GetShortString(var AValue: shortstring);
  9.     procedure TestWrite;
  10.     procedure Free; inline;
  11.   end;      

Code: Pascal  [Select][+][-]
  1.   constructor TPointerHelper.createBuffer(AValue: nativeuint);
  2.   begin
  3.     GetMem(Self, SizeOf(Pointer) * 2 + Avalue);
  4.   end;
  5.  
  6.   constructor TPointerHelper.createLongInt(AValue: longint);
  7.   begin
  8.     GetMem(Self, SizeOf(Pointer) * 2 + SizeOf(longint));
  9.     Move(AValue, TPointerArray(Self)[2], SizeOf(longint));
  10.   end;
  11.  
  12.   constructor TPointerHelper.createShortString(AValue: shortstring);
  13.   begin
  14.     GetMem(Self, SizeOf(Pointer) * 2 + SizeOf(shortstring));
  15.     Move(AValue, TPointerArray(Self)[2], SizeOf(shortstring));
  16.   end;
  17.  
  18.   procedure TPointerHelper.InitPrev(AValue: Pointer);
  19.   begin
  20.     TPointerArray(Self)[0] := AValue;
  21.   end;
  22.  
  23.   function TPointerHelper.GetPrev: Pointer;
  24.   begin
  25.     Result := TPointerArray(Self)[0];
  26.   end;
  27.  
  28.   procedure TPointerHelper.GetLongint(var AValue: longint);
  29.   begin
  30.     Move(TPointerArray(Self)[2], AValue, SizeOf(longint));
  31.   end;
  32.  
  33.   procedure TPointerHelper.GetShortString(var AValue: shortstring);
  34.   begin
  35.     Move(TPointerArray(Self)[2], AValue, SizeOf(shortstring));
  36.   end;
  37.  
  38.  
  39.   procedure TPointerHelper.TestWrite;
  40.   type
  41.     Ar = array [0..0] of byte;
  42.   var
  43.     i: nativeuint;
  44.   begin
  45.     for i := 0 to Memsize(Self) - 1 do Write(Ar(Self^)[i], ' ');
  46.   end;
  47.  
  48.  
  49.   procedure TPointerHelper.Free;
  50.   begin
  51.     FreeMem(Self);
  52.   end;
  53.  
  54.  


MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: "class" without inheritance
« Reply #12 on: September 15, 2022, 08:38:26 am »
see the minimum VMT size for each class in objpash.inc, in the assembler text, or in  https://www.freepascal.org/docs-html/current/prog/progsu166.html#x210-2240008.2.13
And this is only references and without a lot of dolnitel character information, like class name...
Is it really necessary, for a bunch of tiny classes encapsulating only one byte, for example?

My understanding is that that is per /class/ rather than per /instance/. Table 8.9 shows the instance, and all instances of a class share the VMT shown in table 8.10.

Be careful with helpers. I'm not sure the current (3.2.2) situation, but when introduced they had to be organised in a strictly linear sequence: you couldn't tree two helpers off the same class etc.

MarkMLl
« Last Edit: September 15, 2022, 08:43:32 am by 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

PascalDragon

  • Hero Member
  • *****
  • Posts: 5481
  • Compiler Developer
Re: "class" without inheritance
« Reply #13 on: September 15, 2022, 08:55:17 am »
So, it's either "class" which, because of TClass always draws unbelievable amount of trash into each list item in the form of completely unnecessary inherited table of huge number of virtual methods.
Ridiculous.

see the minimum VMT size for each class in objpash.inc, in the assembler text, or in  https://www.freepascal.org/docs-html/current/prog/progsu166.html#x210-2240008.2.13
And this is only references and without a lot of dolnitel character information, like class name...
Is it really necessary, for a bunch of tiny classes encapsulating only one byte, for example?

But the VMT exists only once per type, not per instance. Each instance will have a pointer to that same VMT. Just use TObject.InstanceSize to retrieve the size for an instance of a given class and that will really be the size per instance.

Be careful with helpers. I'm not sure the current (3.2.2) situation, but when introduced they had to be organised in a strictly linear sequence: you couldn't tree two helpers off the same class etc.

By default only one helper can be active for a given type at a type however a helper can descend from an existing helper for the same type (or in case of helpers for classes for a parent class). Additionally in 3.3.1 one can use {$modeswitch MultiHelpers} and that will allow that multiple helpers (using usual scope rules) are active for a type.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6686
Re: "class" without inheritance
« Reply #14 on: September 15, 2022, 09:09:26 am »
But the VMT exists only once per type, not per instance. Each instance will have a pointer to that same VMT. Just use TObject.InstanceSize to retrieve the size for an instance of a given class and that will really be the size per instance.

Thanks for that, I was hoping you were around to confirm or correct me.

My recollection is that there's a small per-instance difference depending on whether something's been defined as

Code: Pascal  [Select][+][-]
  1.       TBDItem = class
  2.         AddSeq: integer; // Anything here
  3.         Prev, Next: TBDItem;
  4.         procedure UnlinkAndFree(aBDList: TBDList);
  5.       end;
  6.  

or

Code: Pascal  [Select][+][-]
  1.       TBDItem = class(TObject)
  2.         AddSeq: integer; // Anything here
  3.         Prev, Next: TBDItem;
  4.         procedure UnlinkAndFree(aBDList: TBDList);
  5.       end;
  6.  

...can you confirm or correct?

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