Recent

Author Topic: Class helpers  (Read 1080 times)

MarkMLl

  • New member
  • *
  • Posts: 34
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



ASBzone

  • Full Member
  • ***
  • Posts: 225
  • Automation leads to relaxation...
    • BrainWaveCC Utilities
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.0.3 r61198 / FPC v3.2.0-beta-r42126 (via FpcUpDeluxe) -- Windows 64-bit install w/32-bit cross-compile
Primary System: Windows 10 Pro x64, Version 1903 (Build 18362.116)
Other Systems: Windows 10 Pro x64, Version 1809 or greater

furious programming

  • Sr. Member
  • ****
  • Posts: 328
  • I click a little.
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 2.0.2 with FPC 3.0.4, Windows XP (all 32-bit)

MarkMLl

  • New member
  • *
  • Posts: 34
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

furious programming

  • Sr. Member
  • ****
  • Posts: 328
  • I click a little.
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 2.0.2 with FPC 3.0.4, Windows XP (all 32-bit)

MarkMLl

  • New member
  • *
  • Posts: 34
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

PascalDragon

  • Sr. Member
  • ****
  • Posts: 353
  • 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

  • New member
  • *
  • Posts: 34
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


Thaddy

  • Hero Member
  • *****
  • Posts: 8020
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....
Hamlet 1.4 (nothing wrong with the Danish, btw)

MarkMLl

  • New member
  • *
  • Posts: 34
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

PascalDragon

  • Sr. Member
  • ****
  • Posts: 353
  • 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

  • Sr. Member
  • ****
  • Posts: 353
  • 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

  • Sr. Member
  • ****
  • Posts: 328
  • I click a little.
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 2.0.2 with FPC 3.0.4, Windows XP (all 32-bit)

jamie

  • Hero Member
  • *****
  • Posts: 1552
Re: Class helpers
« Reply #13 on: May 12, 2019, 03:06:57 pm »
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!
 :)

PascalDragon

  • Sr. Member
  • ****
  • Posts: 353
  • Compiler Developer
Re: Class helpers
« Reply #14 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