Recent

Author Topic: Constant variables specific to inheritance level but not the instance ?  (Read 5853 times)

Nitorami

  • Sr. Member
  • ****
  • Posts: 481
In my base Class, I have a protected static method which needs to check whether an argument is within an allowed range. This range "fMaxInConns" is not specific to the instance, but to the inheritance level. Up to now I used a normal variable fMaxInConns:integer; in the Class' namespace and initialised it within create, but this is waste of space as the variable is unnecessarily stored with each instance.

I tried Class vars instead, but these are simply global and stored once only, hence cannot be different over the Class hierarchy.

Is there a way between ? Something like class vars but which can be redefined in inherited Classes ?





howardpc

  • Hero Member
  • *****
  • Posts: 4144
One possibility is to do something like this:
Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. {$Mode objfpc}{$H+}
  4.  
  5. type
  6.  
  7. TBase = class(TObject)
  8. private
  9.   function GetMaxInConns: Integer; virtual; abstract;
  10. protected
  11.   property MaxInConns: Integer read GetMaxInConns;
  12. end;
  13.  
  14. TFirstBase = class(TBase)
  15.   function GetMaxInConns: Integer; override;
  16. end;
  17.  
  18. TSecondBase = class(TFirstBase)
  19.   function GetMaxInConns: Integer; override;
  20. end;
  21.  
  22. function TSecondBase.GetMaxInConns: Integer;
  23. begin
  24.   Exit(17);
  25. end;
  26.  
  27. function TFirstBase.GetMaxInConns: Integer;
  28. begin
  29.   Exit(12);
  30. end;
  31.  
  32. var
  33.   first: TFirstBase;
  34.   second: TSecondBase;
  35. begin
  36.   first := TFirstBase.Create;
  37.   second := TSecondBase.Create;
  38.   try
  39.     WriteLn('first.MaxInConns = ', first.MaxInConns);
  40.     WriteLn('second.MaxInConns = ', second.MaxInConns);
  41.   finally
  42.     first.Free;
  43.     second.Free;
  44.   end;
  45.   ReadLn;
  46. end.
  47.  

Nitorami

  • Sr. Member
  • ****
  • Posts: 481
Agreed, that would be a solution, it's just a bit more coding than plain constants would require. And I may not forget to override the function in each Class which introduces a change to the parameter.

howardpc

  • Hero Member
  • *****
  • Posts: 4144
...And I may not forget to override the function in each Class which introduces a change to the parameter.
Assuming you meant "may not remember"...
I don't see that remembering to override a virtual function is any different from remembering to override a virtual constructor that initialises a private variable (or reintroducing a static constructor that does the same).

sash

  • Sr. Member
  • ****
  • Posts: 366
Code: Pascal  [Select][+][-]
  1. function TMyClass.GetMyConst : integer; //virtual
  2. begin
  3.   Result := 1;
  4. end;
  5.  
  6. // or
  7.  
  8. function TMyClass.IsMyConstMatching : boolean; //virtual
  9. begin
  10.   Result := (TestVar = 2); // I don't like it, looks more like `magic number` approach
  11. end;
Lazarus 2.0.10 FPC 3.2.0 x86_64-linux-gtk2 @ Ubuntu 20.04 XFCE

Nitorami

  • Sr. Member
  • ****
  • Posts: 481
Quote
Assuming you meant "may not remember"...
Yes, should have said "I must not forget..."  ::)

Quote
I don't see that remembering to override a virtual function is any different from remembering to override a virtual constructor that initialises a private variable (or reintroducing a static constructor that does the same).
In theory yes, but I am lazy and usually copy the constructor initially from the ancestor Class, and there it is obvious that I should amend the values.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Is there a way between ? Something like class vars but which can be redefined in inherited Classes ?
A class var can be a list....or better a tree..
« Last Edit: July 22, 2018, 07:41:09 pm by Thaddy »
Specialize a type, not a var.

Nitorami

  • Sr. Member
  • ****
  • Posts: 481
Quote
A class var can be a list....or better a tree..
Interesting idea. Each Class would then retrieve the specific data from the list via its ClassType ?

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Indeed. I use such a pattern myself.

other option:
How about:
Code: Pascal  [Select][+][-]
  1. program untitled;
  2. {$ifdef fpc}{$mode delphi}{$H+}{$endif}
  3. type
  4.   TBaseclass<T> = class abstract
  5.   public
  6.      class var level:T;
  7.   end;
  8.  
  9.   TFirstRange = 10..19;
  10.   TFirstclass = class(TBaseClass<TFirstRange>)
  11.   end;
  12.  
  13.   TSecondRange = 20..29;
  14.   TSecondclass = class(TBaseClass<TSecondRange>)
  15.   end;
  16.    
  17.   // deeper inheritance:
  18.   TThirdClass<T>= class(TBaseClass<T>)
  19.   end;
  20.  
  21.   TFourthRange = 30..49;
  22.   TFourthClass = class(TThirdClass<TFourthRange>)
  23.   end;
  24.    
  25. begin
  26.  writeln(Low(TFirstClass.Level));
  27.  writeln(Low(TSecondClass.Level));
  28.  writeln(Low(TFourthClass.Level));
  29. end.
That might also do the trick.
You can use the existing inheritance and specify the range as a generic.

« Last Edit: July 23, 2018, 06:46:38 am by Thaddy »
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Here's a more complete example to demonstrate efficient use and inheritance technique.
This is about TRUE class vars based on inheritance level. Not a work around!
Code: Pascal  [Select][+][-]
  1. {$ifdef fpc}{$mode delphi}{$H+}{$endif}
  2. // demo's how one can use generics to declare a per class variable or const
  3. // using generics that depends on the inheritance level.
  4. // here the custom classes are all unspecialized in the hierarchy
  5. // and specialized once completely defined for use.
  6. type
  7.   TBaseclass<T> = class abstract
  8.   public
  9.      class var level:T;
  10.      // add common class methods that manipulate T here
  11.   end;
  12.  
  13.   // Keep the custom classes unspecialized
  14.   TCustomFirstClass<T> = class(TBaseClass<T>)
  15.   // add methods that are not shared with Parent;
  16.   end;
  17.  
  18.   // declare the relevant range for the class
  19.   TFirstRange = 10..19;
  20.   // declare the typed specialized class for ease of use
  21.   // of course you can also specialize a variable of the type
  22.   TFirstclass = class(TCustomFirstClass<TFirstRange>)
  23.   end;
  24.  
  25.   // derive from any custom class
  26.   TSecondRange = 20..29;
  27.   TCustomSecondClass<T> = class(TCustomFirstClass<T>);
  28.   TSecondclass = class(TCustomSecondClass<TSecondRange>)
  29.   end;
  30.    
  31.   TCustomThirdClass<T> = class(TCustomSecondClass<T>)
  32.   end;
  33.  
  34.   TThirdRange = 30..49;
  35.   TThirdClass = class(TCustomThirdClass<TThirdRange>)
  36.   end;
  37.  
  38. var
  39.   // You can also specialize a custom class on use, of course
  40.   FourthClass:TCustomSecondClass<integer>;      
  41. begin
  42.   // hey presto: class variables that depend on the inheritance level.
  43.   writeln(Low(TFirstClass.Level));  
  44.   writeln(Low(TSecondClass.Level));
  45.   writeln(Low(TThirdClass.Level));
  46.   writeln(FourthClass.Level);
  47. end.

You can use a more concise syntax, but the above example let's you manipulate an existing class hierarchy by making a small change into generic custom classes.
E.G. TOldclas = class(TOlderClass) becomes declared as TCustomOldClass<T>=class (TCustomOlderClass<T>), TOldClass = TCustomOldClass<whateverRange>
And all older methods and inheritance keeps working with the advantage of a true class var on per class level basis.
The specialized classes can even have the same signature as your old code.

Which answers the question  :D :D :D 8-) 8-) O:-)
« Last Edit: July 23, 2018, 07:44:56 am by Thaddy »
Specialize a type, not a var.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
@Nitorami:
What do you think? I could convert a similar library to this concept very quickly and it feels natural.
Specialize a type, not a var.

Nitorami

  • Sr. Member
  • ****
  • Posts: 481
Hi Thaddy

*scratching my head* I must confess I have not worked with generics so far, and need to chew on this and digest it first  :D

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Let me know your progress and I am always available to help.
Frankly I did not know this was possible too, until I researched it. O:-) :D.

But it is syntactically and computational correct: It does what you want.

And it is only declarations, not (much) real code....

In general: a request for comments: this is a pattern that can be solved multiple ways.
And the question is rather common. So is the generic way right?
 (I am sure it is, but respect comments from peers, which means you have to be really good).
« Last Edit: July 23, 2018, 10:31:50 pm by Thaddy »
Specialize a type, not a var.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Just as a heads up: best not to use class variables with generics currently, at least until #28911 is solved.

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Just as a heads up: best not to use class variables with generics currently, at least until #28911 is solved.
I don't think my code should be affected. Or am I wrong, Sven?
Specialize a type, not a var.

 

TinyPortal © 2005-2018