Recent

Author Topic: Usages of class methods  (Read 649 times)

egsuh

  • Hero Member
  • *****
  • Posts: 1494
Usages of class methods
« on: October 17, 2024, 11:24:32 am »
This is not a question on write or wrong thing, rather on approach.

Recently I came up to think about class methods. In my application, some methods seem to be definitely better to be defined as class methods, which do not have any access to the internal fields.

But some methods need to define codes both for instantiated object and nil object. Well, I can define it as global function/procedure, but semantically better tied to a class. So, I'm thinking following way:

Code: Pascal  [Select][+][-]
  1. type
  2.    { TMyClass }
  3.  
  4.    TMyClass = class
  5.    private
  6.       a, b : integer;
  7.  
  8.    public
  9.       class procedure ShowValue(var AValue: integer);
  10.       class procedure ShowValue2(AValue: integer; AClass:TMyClass = nil);
  11.    end;
  12.  
  13.    { TForm1 }
  14.  
  15.    TForm1 = class(TForm)
  16.       Button2: TButton;
  17.       procedure Button2Click(Sender: TObject);
  18.    end;
  19. var
  20.    Form1: TForm1;    
  21.  
  22. implementation
  23.  
  24. {$R *.lfm}
  25.  
  26. { TMyClass }
  27.  
  28. class procedure TMyClass.ShowValue(var AValue: integer);
  29. begin
  30.    ShowMessage(IntToStr(Avalue));
  31. end;
  32.  
  33. class procedure TMyClass.ShowValue2(AValue: integer; AClass: TMyClass);
  34. begin
  35.    if AClass <> nil then AClass.A := AValue;
  36.     ShowMessage(IntToStr(AValue));
  37. end;
  38.  
  39. procedure TForm1.Button2Click(Sender: TObject);
  40. var
  41.    MyClass: TMyClass;
  42.    i: integer;
  43. begin
  44.    i:= 9876;
  45.    TMyClass.ShowValue(i);  // I CANNOT call Myclass.showvalue here.
  46.  
  47.    MyClass := TMyClass.Create;
  48.    MyClass.A := 123;
  49.    MyClass.ShowValue(MyClass.A);
  50.    TMyClass.ShowValue2(i, MyClass); //  I can also call MyClass.ShowValue2
  51.    MyClass.ShowValue(MyClass.A);
  52.    MyClass.Free;
  53. end;
  54.  
  55. end.
  56.  

The key is procedure ShowValue2.

Well this example compiles and runs well. I get what I want to. 

Are there any foreseeable problems which I cannot think of right now?
If you have any experience of this approach then please advise me. Or is this popular usage?
I'd like to listen to your opinions before I change my codes, even though they are not very much volume.

Zvoni

  • Hero Member
  • *****
  • Posts: 2754
Re: Usages of class methods
« Reply #1 on: October 17, 2024, 11:38:21 am »
Alternative....
Code: Pascal  [Select][+][-]
  1. program Project1;
  2. {$mode objfpc}{$H+}
  3. Uses Classes;
  4. Type
  5.   TMyClass = Class
  6.     Private
  7.       a:Integer;
  8.       b:Integer;
  9.     Public
  10.       Class Procedure ShowValue(var AValue:Integer);Overload;
  11.       Class Procedure ShowValue(AValue:Integer; AClass:TMyClass);Overload;
  12.   end;
  13. Var
  14.    MyClass:TMyClass;
  15.    i:Integer;
  16.  
  17. class procedure TMyClass.ShowValue(var AValue: Integer);
  18. begin
  19.   Writeln('AValue from single arg-proc=', AValue);
  20. end;
  21.  
  22. class procedure TMyClass.ShowValue(AValue: Integer; AClass: TMyClass);
  23. begin
  24.   If Assigned(AClass) Then AClass.a:=AValue;
  25.   Writeln('AValue from double arg-proc=', AValue);
  26. end;
  27.  
  28. begin
  29.   i:=9876;
  30.   TMyClass.ShowValue(i);
  31.   MyClass:=TMyClass.Create;
  32.   MyClass.A:=123;
  33.   MyClass.ShowValue(MyClass.A);
  34.   TMyClass.ShowValue(i, MyClass);
  35.   MyClass.ShowValue(MyClass.A);
  36.   MyClass.Free;
  37. end.
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

Thaddy

  • Hero Member
  • *****
  • Posts: 16201
  • Censorship about opinions does not belong here.
Re: Usages of class methods
« Reply #2 on: October 17, 2024, 11:39:20 am »
where it fails is that the field is not declared as class var.
note that class methods work just for class variables, so work on every instance on the class with the same value;
Code: Pascal  [Select][+][-]
  1. {$mode objfpc}
  2. type
  3.    { TMyClass }
  4.  
  5.    TMyClass = class
  6.    private
  7.       class var a, b : integer;
  8.  
  9.    public
  10.       class procedure ShowValue(var AValue: integer);
  11.       class procedure ShowValue2(AValue: integer; AClass:TMyClass = nil);
  12.    end;
  13.  
  14. { TMyClass }
  15.  
  16. class procedure TMyClass.ShowValue(var AValue: integer);
  17. begin
  18.    writeln(Avalue);
  19. end;
  20.  
  21. class procedure TMyClass.ShowValue2(AValue: integer; AClass: TMyClass);
  22. begin
  23.    // if AClass <> nil then AClass.A := AValue; // this is nonsense
  24.    writeln(AValue);
  25. end;
  26.  
  27. var
  28.    MyClass: TMyClass;
  29.    i: integer;
  30. begin
  31.    i:= 9876;
  32.    TMyClass.ShowValue(i);  // of course you can
  33.     MyClass := TMyClass.Create;
  34.    MyClass.A := 123;
  35.    MyClass.ShowValue(MyClass.A);
  36.    TMyClass.ShowValue2(i, MyClass); // same
  37.    MyClass.ShowValue(MyClass.A);
  38.    MyClass.Free;
  39. end.
It seems to me that you do not fully understand class methods.
Ask...
If I smell bad code it usually is bad code and that includes my own code.

Thaddy

  • Hero Member
  • *****
  • Posts: 16201
  • Censorship about opinions does not belong here.
Re: Usages of class methods
« Reply #3 on: October 17, 2024, 11:41:50 am »
the answer is probably between Zvoni's (post crossed) and mine. It depends on what you want to achieve.
If I smell bad code it usually is bad code and that includes my own code.

MarkMLl

  • Hero Member
  • *****
  • Posts: 8045
Re: Usages of class methods
« Reply #4 on: October 17, 2024, 12:15:37 pm »
Recently I came up to think about class methods. In my application, some methods seem to be definitely better to be defined as class methods, which do not have any access to the internal fields.

It's not so much that class methods don't have access to internal fields, as that they don't have access to /instantiated/ fields.

I spent a chunk of the Summer doing some stuff related to mapping, and ended up with (very roughly) classes describing paths and areas all relative to a local origin (observer) where class fields and methods were a natural way of saying "For all the paths in this program, /this/ is the local origin which should be used when telling the observer where to look".

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Khrys

  • Full Member
  • ***
  • Posts: 109
Re: Usages of class methods
« Reply #5 on: October 17, 2024, 01:43:12 pm »
Note that class methods still get an implicit  Self  parameter pointing to the type's virtual method table (VMT) instead of an instance.
You can add  static;  at the end of a class method definition to make it truly equivalent to a free-standing function while retaining name scoping.

Are there any foreseeable problems which I cannot think of right now?

This most likely won't be an issue, but you can't call class methods on  Nil  (even though it's possible for regular methods in some cases). See the following comparison:

Code: Pascal  [Select][+][-]
  1. type
  2.   TTest = class
  3.     procedure Method();
  4.     class procedure ClassMethod();
  5.     class procedure ClassMethodStatic(); static;
  6.   end;
  7.  
  8. procedure TTest.Method();
  9. begin
  10.   WriteLn(HexStr(Self)); // Prints instance
  11. end;
  12.  
  13. class procedure TTest.ClassMethod();
  14. begin
  15.   WriteLn(HexStr(Self)); // Prints pointer to VMT (type)
  16. end;
  17.  
  18. class procedure TTest.ClassMethodStatic();
  19. begin
  20.   WriteLn('static'); // There is no 'Self' parameter
  21. end;
  22.  
  23. var
  24.   Instance: TTest;
  25. begin
  26.   // Call on instance
  27.   Instance := TTest.Create();
  28.   Instance.Method();              // OK
  29.   Instance.ClassMethod();         // OK
  30.   Instance.ClassMethodStatic();   // OK
  31.   Instance.Free();
  32.  
  33.   // Call on Nil
  34.   TTest(Nil).Method();            // OK as long as no members are accessed
  35.   { TTest(Nil).ClassMethod(); }   // Doesn't work - attempts to dereference VMT (implicit first member)
  36.   TTest(Nil).ClassMethodStatic(); // OK
  37.  
  38.   // Call on type
  39.   { TTest.Method(); }             // Doesn't work - requires instance
  40.   TTest.ClassMethod();            // OK
  41.   TTest.ClassMethodStatic();      // OK
  42. end.

Thaddy

  • Hero Member
  • *****
  • Posts: 16201
  • Censorship about opinions does not belong here.
Re: Usages of class methods
« Reply #6 on: October 17, 2024, 08:21:18 pm »
@egsuh
can you describe the use case you have in mind because all the above answers are correct.
I still think you do not know what class methods do...
If I smell bad code it usually is bad code and that includes my own code.

Martin_fr

  • Administrator
  • Hero Member
  • *
  • Posts: 10561
  • Debugger - SynEdit - and more
    • wiki
Re: Usages of class methods
« Reply #7 on: October 17, 2024, 09:04:22 pm »
If this is just about that your variable can be nil.

Code: Pascal  [Select][+][-]
  1.     type
  2.        TMyClass = class
  3.        private
  4.           a, b : integer;
  5.        public
  6.           procedure ShowValue(AValue: integer);
  7.        end;
  8.  
  9.     implementation
  10.      
  11.     class procedure TMyClass.ShowValue(AValue: integer);
  12.     begin
  13.        if Self <> nil then {Self.}A := AValue;
  14.         ShowMessage(IntToStr(AValue));
  15.     end;
  16.  
  17. var
  18.   foo: TMyClass;    
  19. begin
  20.        i:= 9876;
  21.  
  22.        foo := nil;
  23.        foo.ShowValue(i);  
  24.  
  25.        foo := TMyClass.Create;
  26.        foo.ShowValue(i);  
  27. end;
  28.      
  29. end.
  30.  

Of course you need to initialize foo with nil. Otherwise it is random and that will crash.




class procedures are for example used with class variables
Code: Pascal  [Select][+][-]
  1.     type
  2.        TMyClass = class
  3.          class procedure foo; virtual;
  4.        end;
  5.  
  6.        TMySubClass = class(TMyClass)
  7.          class procedure foo; override;
  8.        end;
  9.  
  10.        TMyClassClass = class of TMyClass;
  11.  
  12. var
  13.   bar: TMyClassClass;
  14. begin
  15.    bar := TMyClass;  // the class not an instance
  16.    bar.foo;
  17.    bar := TMySubClass;
  18.    bar.foo;
  19. end;
  20.  


egsuh

  • Hero Member
  • *****
  • Posts: 1494
Re: Usages of class methods
« Reply #8 on: October 18, 2024, 05:35:09 am »
Quote
can you describe the use case you have in mind because all the above answers are correct.
I still think you do not know what class methods do...

I think I know what class methods do.  I understand all the examples, except following.

 
Code: Pascal  [Select][+][-]
  1.    type
  2.        TMyClass = class
  3.        private
  4.           a, b : integer;
  5.        public
  6.           procedure ShowValue(AValue: integer);
  7.        end;
  8.  
  9.     implementation
  10.      
  11.     class procedure TMyClass.ShowValue(AValue: integer);   // in the type definition, this is not class procedure
  12.     begin
  13.        if Self <> nil then {Self.}A := AValue;   // this doesn't work as A is not a class field.
  14.         ShowMessage(IntToStr(AValue));
  15.     end;

In the above example, I think Martin missed declaring var a  and procedure ShowValue as class var and class procedure.

Use case...  I wrote many methods (not class methods) for class, and the data (field values) are stored in database.  And now I'd like to call some methods without creating the object, only with a few field data from DB. Of course I can create a dummy object, but looking for other ways. I'm thinking of SELECTing only necessary fields, and defining the methods as class methods so that I do not have to  create dummy object. I might have to define some of the fields as class var as well.

I may define the methods as general independent function/procedure, but those methods had better be linked to the class, as I told. For example, when there are any changes in the DB structure, it's better to keep those methods within classes rather than defined as independent function/procedure, from maintenance point of view.

This is for my cognition, not for computing efficiency. I'm at the stage of pondering, not doing anything yet.

This may not be the intended purpose of class methods, but I don't think we have to be functionally fixed. We can step on a stool to exchange a bulb, not only sit on it.
« Last Edit: October 18, 2024, 05:53:51 am by egsuh »

Thaddy

  • Hero Member
  • *****
  • Posts: 16201
  • Censorship about opinions does not belong here.
Re: Usages of class methods
« Reply #9 on: October 18, 2024, 06:11:52 am »
Yes, correct, class methods work only with class vars as per my first example.
Using a record with all static class methods is actually quite a common pattern.
If you make the class methods also static, you treat them as normal procedures.
They have essentially the same signature.
See an example, written by me:
https://wiki.freepascal.org/Marsaglia%27s_pseudo_random_number_generators
« Last Edit: October 18, 2024, 06:27:12 am by Thaddy »
If I smell bad code it usually is bad code and that includes my own code.

 

TinyPortal © 2005-2018