Recent

Author Topic: Class helpers  (Read 6961 times)

MarkMLl

  • Hero Member
  • *****
  • Posts: 6646
Class helpers
« on: May 01, 2019, 06:44:26 pm »
Two questions. The first: is there a restriction that a class can only be extended by a single helper, after which it's the helper that has to be extended? i.e.

A= class
B= class helper for A
C= class helper for A

is wrong, while

A= class
B= class helper for A
C= class helper(B) for A

is right? Alternatively, is there a rule that only one helper can be declared in a unit?

The problem that I was having was that when compiling using the Lazarus IDE it would fail at the first point it encountered a method from helper B, rather than getting into the unit B and saying "You can't declare C in here".

The second: Can the definition of a class list the names of the units in which it may be extended by a helper?

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

ASBzone

  • Hero Member
  • *****
  • Posts: 678
  • Automation leads to relaxation...
    • Free Console Utilities for Windows (and a few for Linux) from BrainWaveCC
Re: Class helpers
« Reply #1 on: May 01, 2019, 07:26:45 pm »
Two questions. The first: is there a restriction that a class can only be extended by a single helper, after which it's the helper that has to be extended? i.e.
...

You may receive better assistance with an actual code block than with a theoretical question.
-ASB: https://www.BrainWaveCC.com/

Lazarus v2.2.7-ada7a90186 / FPC v3.2.3-706-gaadb53e72c
(Windows 64-bit install w/Win32 and Linux/Arm cross-compiles via FpcUpDeluxe on both instances)

My Systems: Windows 10/11 Pro x64 (Current)

furious programming

  • Hero Member
  • *****
  • Posts: 836
Re: Class helpers
« Reply #2 on: May 01, 2019, 08:09:42 pm »
See the example:

Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}{$LONGSTRINGS ON}{$MODESWITCH TYPEHELPERS}
  2.  
  3. type
  4.   TIntegerHelper = type helper for Integer
  5.   public
  6.     procedure Foo();
  7.   end;
  8.  
  9.   procedure TIntegerHelper.Foo();
  10.   begin
  11.     WriteLn('Integer.Foo');
  12.   end;
  13.  
  14. type
  15.   TAnotherIntegerHelper = type helper for Integer
  16.   public
  17.     procedure Bar();
  18.   end;
  19.  
  20.   procedure TAnotherIntegerHelper.Bar();
  21.   begin
  22.     WriteLn('Integer.Bar');
  23.   end;
  24.  
  25. var
  26.   Value: Integer = 0;
  27. begin
  28.   Value.Foo();
  29.   Value.Bar();
  30. end.

This code does not compile — Foo method is not known for the compiler, because TIntegerHelper is covered by the TAnotherIntegerHelper, so the selected line is invalid. In result, whole content of the TIntegerHelper is not accessible.

So if you want to completely cover the existing helper, don't use inheritance. But if you want to use methods from both helpers, use the inheritance:

Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}{$LONGSTRINGS ON}{$MODESWITCH TYPEHELPERS}
  2.  
  3. type
  4.   TIntegerHelper = type helper for Integer
  5.   public
  6.     procedure Foo();
  7.   end;
  8.  
  9.   procedure TIntegerHelper.Foo();
  10.   begin
  11.     WriteLn('Integer.Foo');
  12.   end;
  13.  
  14. type
  15.   TAnotherIntegerHelper = type helper(TIntegerHelper) for Integer
  16.   public
  17.     procedure Bar();
  18.   end;
  19.  
  20.   procedure TAnotherIntegerHelper.Bar();
  21.   begin
  22.     WriteLn('Integer.Bar');
  23.   end;
  24.  
  25. var
  26.   Value: Integer = 0;
  27. begin
  28.   Value.Foo();
  29.   Value.Bar();
  30. end.
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6646
Re: Class helpers
« Reply #3 on: May 01, 2019, 08:24:11 pm »
So if you want to completely cover the existing helper, don't use inheritance. But if you want to use methods from both helpers, use the inheritance:

Thanks for that, so in effect helpers are a linked list with no provision for branches.

Looking back through the record I can see that when class sealing was introduced I asked on the ML whether a sealed class could be extended by a helper. Nobody had a confident answer to that, but I think it fair to also ask whether a chain of class helpers can be sealed to prevent anybody extending it further.

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

furious programming

  • Hero Member
  • *****
  • Posts: 836
Re: Class helpers
« Reply #4 on: May 01, 2019, 10:26:24 pm »
Looking back through the record I can see that when class sealing was introduced I asked on the ML whether a sealed class could be extended by a helper.

Of course, sealed classes can be extended by helpers. Example:

Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}{$LONGSTRINGS ON}{$MODEsWITCH TYPEHELPERS}
  2.  
  3. type
  4.   TThing = class sealed(TObject)
  5.   public
  6.     procedure Foo();
  7.   end;
  8.  
  9.   procedure TThing.Foo();
  10.   begin
  11.     WriteLn('TThing.Foo');
  12.   end;
  13.  
  14. type
  15.   TThingHelper = class helper for TThing
  16.   public
  17.     procedure Bar();
  18.   end;
  19.  
  20.   procedure TThingHelper.Bar();
  21.   begin
  22.     WriteLn('TThing.Bar');
  23.   end;
  24.  
  25. type
  26.   TThingAnotherHelper = class helper(TThingHelper) for TThing
  27.   public
  28.     procedure Baz();
  29.   end;
  30.  
  31.   procedure TThingAnotherHelper.Baz();
  32.   begin
  33.     WriteLn('TThing.Baz');
  34.   end;
  35.  
  36. var
  37.   Thing: TThing;
  38. begin
  39.   Thing := TThing.Create();
  40.   try
  41.     Thing.Foo();
  42.     Thing.Bar();
  43.     Thing.Baz();
  44.   finally
  45.     Thing.Free();
  46.   end;
  47. end.

Quote
Nobody had a confident answer to that, but I think it fair to also ask whether a chain of class helpers can be sealed to prevent anybody extending it further.

Sealing of classes are used only to block inheritance when creating a new data type (new class). Helpers are used to extend classes (and any other types of data), where inheritance does not occur at all, so the seal in this case does not matter.

Interestingly, it is not possible to seal the helper. Thus, each type can be extended with helpers, and their number is not limited. It is therefore possible to create a theoretically freely long chain of helper inheritance and extend the given type of data without end.
« Last Edit: May 01, 2019, 10:30:24 pm by furious programming »
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6646
Re: Class helpers
« Reply #5 on: May 01, 2019, 10:44:03 pm »
Of course, sealed classes can be extended by helpers. Example:
...
Sealing of classes are used only to block inheritance when creating a new data type (new class). Helpers are used to extend classes (and any other types of data), where inheritance does not occur at all, so the seal in this case does not matter.

Interestingly, it is not possible to seal the helper. Thus, each type can be extended with helpers, and their number is not limited. It is therefore possible to create a theoretically freely long chain of helper inheritance and extend the given type of data without end.

Thanks for that. I did say nobody had, and this was a few years ago :-)

I tend to look at things from the point of view of an OS writer, but I'd have thought that being able to forcibly terminate a helper chain would be useful. Or alternatively the second part of my original question, which was whether helpers could be rejected if they weren't in a list of specified units- which is probably infeasible, since I can't think of anywhere that a list of units is significant except  uses  clauses.

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: 5444
  • Compiler Developer
Re: Class helpers
« Reply #6 on: May 02, 2019, 09:26:02 am »
Two questions. The first: is there a restriction that a class can only be extended by a single helper, after which it's the helper that has to be extended? i.e.

A= class
B= class helper for A
C= class helper for A

is wrong, while

A= class
B= class helper for A
C= class helper(B) for A

is right? Alternatively, is there a rule that only one helper can be declared in a unit?

The problem that I was having was that when compiling using the Lazarus IDE it would fail at the first point it encountered a method from helper B, rather than getting into the unit B and saying "You can't declare C in here".
You can declare multiple helpers for a class, the compiler will simply pick the last one in scope. If you want to use multiple helpers you need to use inheritance.

Also the compiler would not be able to detect that you extend class A (declared in unit A) in both units B and C (without dependencies on eachother) as those units could be compiled independently (or be precompiled), not to mention that you could use unit B in unit X and unit C in Y those using different helpers for A inside the same binary.

Please note that we're planning to add support for multiple helpers to be active at once. You can keep track of the progress here.

The second: Can the definition of a class list the names of the units in which it may be extended by a helper?
Eh? What are you asking here?

MarkMLl

  • Hero Member
  • *****
  • Posts: 6646
Re: Class helpers
« Reply #7 on: May 02, 2019, 10:24:19 am »
You can declare multiple helpers for a class, the compiler will simply pick the last one in scope. If you want to use multiple helpers you need to use inheritance.

Also the compiler would not be able to detect that you extend class A (declared in unit A) in both units B and C (without dependencies on eachother) as those units could be compiled independently (or be precompiled), not to mention that you could use unit B in unit X and unit C in Y those using different helpers for A inside the same binary.

Please note that we're planning to add support for multiple helpers to be active at once. You can keep track of the progress here.

Thanks for those Sven. I'd picked up the dependencies issue but in this case both helpers were in the same unit which is why I was wondering whether there was a "one helper per unit" rule or something similar.

>> The second: Can the definition of a class list the names of the units in which it may be extended by a helper?
>
> Eh? What are you asking here?

Whether at the time of definition a class can have restrictions as to where helpers can be declared, e.g. that helpers for TObject can only be in known RTL units.

In practice I think that the existing dependency rules probably go a long way towards that, but I think that whether a chain of helpers can be sealed is still a fair question- and probably one that needs to be considered before any relaxation of the structure.

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

Thaddy

  • Hero Member
  • *****
  • Posts: 14159
  • Probably until I exterminate Putin.
Re: Class helpers
« Reply #8 on: May 02, 2019, 10:43:59 am »
Of course, sealed classes can be extended by helpers. Example:
...
Sealing of classes are used only to block inheritance when creating a new data type (new class). Helpers are used to extend classes (and any other types of data), where inheritance does not occur at all, so the seal in this case does not matter.

Interestingly, it is not possible to seal the helper. Thus, each type can be extended with helpers, and their number is not limited. It is therefore possible to create a theoretically freely long chain of helper inheritance and extend the given type of data without end.

Thanks for that. I did say nobody had, and this was a few years ago :-)

I tend to look at things from the point of view of an OS writer, but I'd have thought that being able to forcibly terminate a helper chain would be useful. Or alternatively the second part of my original question, which was whether helpers could be rejected if they weren't in a list of specified units- which is probably infeasible, since I can't think of anywhere that a list of units is significant except  uses  clauses.

MarkMLl
Mark, I hope you understand class helpers are a bandage (except maybe for simple types) and not a cure....
Specialize a type, not a var.

MarkMLl

  • Hero Member
  • *****
  • Posts: 6646
Re: Class helpers
« Reply #9 on: May 02, 2019, 12:27:42 pm »
Mark, I hope you understand class helpers are a bandage (except maybe for simple types) and not a cure....

I understand you perfectly but I'm converting comms code written some time in the last millennium. It's thoroughly bloody, and I need every bit of help I can get :-)

Part of the problem is that it's got multiple UIs: GUI, internal interactive text and internal script which were OK under TP and later Delphi but not Lazarus. I was able to just about get any two of those three working without having to do major re-engineering, but the combination of a scripted activity being able to raise a GUI dialog(ue) box eventually forced me to move actual command execution into a thread. At that point it's convenient to be able to define the guts of the worker thread where the command is parsed, but to extend it using helpers for specific jobs (GUI manipulation, comms line handling). Each helper encapsulates Synchronize() calls and shim procedures, all heavily protected by assertions... I'd be the first to admit that it's hardly pretty but it works and keeps the main part of the worker thread fairly tidy.

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: 5444
  • Compiler Developer
Re: Class helpers
« Reply #10 on: May 03, 2019, 09:13:55 am »
>> The second: Can the definition of a class list the names of the units in which it may be extended by a helper?
>
> Eh? What are you asking here?

Whether at the time of definition a class can have restrictions as to where helpers can be declared, e.g. that helpers for TObject can only be in known RTL units.

In practice I think that the existing dependency rules probably go a long way towards that, but I think that whether a chain of helpers can be sealed is still a fair question- and probably one that needs to be considered before any relaxation of the structure.

The purpose of helpers is to be able to add code to classes that you don't have direct access to. Adding some mechanism that would allow to restrict which units can extend that would be a contradiction to that purpose. Not to mention that in that supposed restriction clause you could enter basically anything, because the compiler can not check whether the unit exists (for example the System unit where TObject is declared is compiled without any other units involved).

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Class helpers
« Reply #11 on: May 11, 2019, 02:41:12 pm »
Trunk now also supports the modeswitch multihelpers which allows multiple helpers to be active at once for a single type. See also here.

furious programming

  • Hero Member
  • *****
  • Posts: 836
Re: Class helpers
« Reply #12 on: May 12, 2019, 12:38:34 am »
Trunk now also supports the modeswitch multihelpers which allows multiple helpers to be active at once for a single type.

Good news, thanks. However, there is still no possibility of creating a helper for classic objects, e.g. in this way:

Code: Pascal  [Select][+][-]
  1. type
  2.   TFoo = object
  3.   public
  4.     procedure Bar();
  5.   end;
  6.  
  7. type
  8.   TFooHelper = object helper for TFoo // Error: expected :, but for found
  9.   public
  10.     procedure Baz();
  11.   end;

Will it ever be supported?
Lazarus 3.2 with FPC 3.2.2, Windows 10 — all 64-bit

Working solo on an acrade, action/adventure game in retro style (pixelart), programming the engine and shell from scratch, using Free Pascal and SDL. Release planned in 2026.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5444
  • Compiler Developer
Re: Class helpers
« Reply #13 on: May 13, 2019, 09:14:14 am »
Will it ever be supported?
Considering that I added support for interface type helpers as well, I could add support for object as well... You can add a bug report if you want...
as it is now I am able to inherit from an existing helper to add on features. From what I understand this is not allowed in
Delphi.

 In Delphi you can have only one helper and any additions must be done to that one helper where as in Fpc you can
inherit from an existing helper which I like a lot!
 :)
Delphi allows inheritance for class helper, but I saw no real reason to disallow it for record helper and type helper...  :-X

BeniBela

  • Hero Member
  • *****
  • Posts: 905
    • homepage
Re: Class helpers
« Reply #14 on: May 14, 2019, 12:46:38 am »
I do not understand why there are so many different helpers

Why not call them all "type helper"  ?

 

TinyPortal © 2005-2018