Recent

Author Topic: Code shaping advice  (Read 1051 times)

Чебурашка

  • Hero Member
  • *****
  • Posts: 568
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Code shaping advice
« on: December 01, 2022, 12:09:55 pm »
Hello I have this problem:

I have 2 methods (in reality it is much more, and the need comes from working with database try catches, if you like I can show the original stuff):

Code: Pascal  [Select][+][-]
  1. procedure MethodA(2 parameters);
  2. begin
  3.   // body of A using the 2 params
  4. end;
  5.  
  6. procedure MethodB(10 parameters);
  7. begin
  8.   // body of B using the 10 params
  9. end;
  10.  

These methods can raise exceptions. And the management of these exceptions is always the same.

Therefore I decide to wrap them in a ExcHandlingWrapped version:

Code: Pascal  [Select][+][-]
  1. procedure ExcHandlingMethodA(N extra parameters necessary for the external part + 2 parameters from the real MethodA);
  2. begin
  3.   // do things before (always the same regardless of MethodA) using the N params
  4.   MethodA(his 2 parameters);
  5.   // do things after and manage exceptions (always the same regardless of MethodA) using the N params
  6. end;
  7.  
  8. procedure ExcHandlingMethodB(N extra parameters necessary for the external part + 10 parameters from the real MethodB);
  9. begin
  10.   // do things before (always the same regardless of MethodB) using the N params
  11.   MethodB(his 10 parameters);
  12.   // do things after and manage exceptions (always the same regardless of MethodB) using the N params
  13. end;
  14.  

I was wondering if is possible directly in the language or someone can suggest me a trick to do something:

Code: Pascal  [Select][+][-]
  1. procedure ExcHandlingUniversal(N extra parameters necessary for the external part, parameterless proc);
  2. begin
  3.   // do things before (always the same regardless of proc) using the N params
  4.   proc();
  5.   // do things after and manage exceptions (always the same regardless of proc) using the N params
  6. end;
  7.  

so that I can do something like:

Code: C#  [Select][+][-]
  1. ExcHandlingUniversal(N parameters, () ->
  2. {
  3.   MethodA(his 2 parameters);
  4. });
  5.  
  6. ExcHandlingUniversal(N parameters, () ->
  7. {
  8.   MethodB(his 10 parameters);
  9. });
  10.  

Sorry for the syntax is somehow mixed with C#, but I hope you understand what I mean.

If I think to pascal I should do something like

Code: Pascal  [Select][+][-]
  1. var
  2.  MethodAP1: type;
  3.  MethodAP2: type;
  4.  
  5. procedure MethodACaller();
  6. begin
  7.   MethodA(MethodAP1, MethodAP2);
  8. end;
  9.  
  10. var
  11.  MethodBP1: type;
  12.  MethodBP2: type;
  13.  // up to 10
  14.  
  15. procedure MethodBCaller();
  16. begin
  17.   MethodB(MethodBP1, MethodBP2, up to 10);
  18. end;
  19.  


and then do something like

Code: Pascal  [Select][+][-]
  1.  
  2.  MethodAP1 := value;
  3.  MethodAP2 := value;
  4.  
  5. ExcHandlingUniversal(N parameters, @MethodACaller);
  6.  
  7.  MethodBP1 := value;
  8.  MethodBP2 := value;
  9.  // up to 10
  10.  
  11. ExcHandlingUniversal(N parameters, @MethodBCaller);
  12.  

Do I don't like so much because it might open problems in case of concurrent threads due to the external variables. And is kinda ugly.

Some idea?
Thank you
« Last Edit: December 01, 2022, 12:18:06 pm by tt »
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

beria

  • Jr. Member
  • **
  • Posts: 70
Re: Code shaping advice
« Reply #1 on: December 01, 2022, 12:31:53 pm »
Maybe I mean something else, but in such cases I always use large static (more often) or dynamic arrays of procedural variables, filled with addresses of all procedures of the same type. And accordingly, I make a common procedure with all the handlings and exceptions, for the whole array....
And yes, shared variables for all threads work through defining them in the "threadvar" section


Чебурашка

  • Hero Member
  • *****
  • Posts: 568
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Re: Code shaping advice
« Reply #2 on: December 01, 2022, 12:39:43 pm »
Maybe I mean something else, but in such cases I always use large static (more often) or dynamic arrays of procedural variables, filled with addresses of all procedures of the same type. And accordingly, I make a common procedure with all the handlings and exceptions, for the whole array....
And yes, shared variables for all threads work through defining them in the "threadvar" section

How do you pass to each of these procedural variables the right (amount of) parameters? I was thinking also to something like this, but I found complicated to pass the variables, unless one creates a object containing the right (amount of) parameters for each single procedural variable.

But thank you anyway for sharing the idea.
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

Zvoni

  • Hero Member
  • *****
  • Posts: 2319
Re: Code shaping advice
« Reply #3 on: December 01, 2022, 12:55:17 pm »
What about open array of const as argument?
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

jamie

  • Hero Member
  • *****
  • Posts: 6090
Re: Code shaping advice
« Reply #4 on: December 01, 2022, 01:07:55 pm »
Open arrays are fine but then it needs to be structured in such a way that it can determine what data is what without hacking it.

 How about using a record that contains information and pass that?
The only true wisdom is knowing you know nothing

Wallaby

  • Jr. Member
  • **
  • Posts: 78
Re: Code shaping advice
« Reply #5 on: December 01, 2022, 01:10:18 pm »
I can think of two ways of doing it.

One is using 'array of const' which is basically for any number of parameters, but you will need to access them by index, which may be hard to understand the code:

Code: Pascal  [Select][+][-]
  1. procedure MethodA(const Params: array of const);
  2. begin
  3.   //do stuff
  4. end;
  5.  
  6. procedure MethodB(const Params: array of const);
  7. begin
  8.   //do stuff
  9. end;
  10.  
  11. procedure CallMyMethod(const Params: array of const; const MyMethod: TMyMethod);
  12. begin
  13.   try
  14.     //do some common stuff
  15.     MyMethod(Params);
  16.   finally
  17.     //and more common stuff here
  18.   end;
  19. end;
  20.  
  21. procedure TForm1.FormCreate(Sender: TObject);
  22. begin
  23.   CallMyMethod(['aaa', 'bbb'], MethodA);
  24.   CallMyMethod([1, 2, 3], MethodB);
  25. end;  

Another one is making parameters fields of an object with a common class, e.g.

Code: Pascal  [Select][+][-]
  1.   TBaseParams = class(TObject);
  2.  
  3.   TParamsForA = class(TBaseParams)
  4.     Param1: string;
  5.     Param2: string;
  6.   end;
  7.   TParamsForB = class(TBaseParams)
  8.     Param1: double;
  9.     Param2: double;
  10.     Param10: double;
  11.   end;
  12.  
  13.   TMyMethod = procedure(const Params: TBaseParams);
  14.  
  15.  
  16. implementation
  17.  
  18. procedure MethodA(const Params: TBaseParams);
  19. var
  20.   MyParams: TParamsForA;
  21. begin
  22.   MyParams := Params as TParamsForA;
  23.   //do stuff
  24. end;
  25.  
  26. procedure MethodB(const Params: TBaseParams);
  27. var
  28.   MyParams: TParamsForB;
  29. begin
  30.   MyParams := Params as TParamsForB;
  31.   //do stuff
  32. end;
  33.  
  34. procedure CallMyMethod(const Params: TBaseParams; const MyMethod: TMyMethod);
  35. begin
  36.   try
  37.     //do some common stuff
  38.     MyMethod(Params);
  39.   finally
  40.     //and more common stuff here
  41.   end;
  42. end;
  43.  
  44. procedure TForm1.FormCreate(Sender: TObject);
  45. var
  46.   ParamsForA: TParamsForA;
  47.   ParamsForB: TParamsForB;
  48. begin
  49.   ParamsForA := TParamsForA.Create;
  50.   ParamsForA.Param1 := 'aaa';
  51.   ParamsForA.Param2 := 'bbb';
  52.   CallMyMethod(ParamsForA, MethodA);
  53.   ParamsForA.Free;
  54.  
  55.   ParamsForB := TParamsForB.Create;
  56.   ParamsForB.Param1 := 1;
  57.   ParamsForB.Param2 := 2;
  58.   ParamsForB.Param10 := 3;
  59.   CallMyMethod(ParamsForB, MethodB);
  60.   ParamsForB.Free;
  61. end;

Alternatively you could use a dictionary or similar structure to pass multiple values in a single argument.
« Last Edit: December 01, 2022, 01:24:18 pm by Wallaby »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Code shaping advice
« Reply #6 on: December 01, 2022, 09:56:05 pm »
Some idea?

Starting with FPC 3.3.1 you can do the following:

Code: Pascal  [Select][+][-]
  1. program ttest;
  2.  
  3. {$mode objfpc}
  4. {$modeswitch functionreferences}
  5. {$modeswitch anonymousfunctions}
  6.  
  7. type
  8.   TProc = reference to procedure;
  9.  
  10. procedure ExceptionWrapper(aProc: TProc);
  11. begin
  12.   try
  13.     aProc();
  14.   except
  15.     // Whatever
  16.   end;
  17. end;
  18.  
  19. procedure Proc1({ arg decl });
  20. begin
  21. end;
  22.  
  23. procedure Proc2({ arg decl });
  24. begin
  25. end;
  26.  
  27. begin
  28.   ExceptionWrapper(procedure begin
  29.     Proc1({ args });
  30.   end);
  31.  
  32.   ExceptionWrapper(procedure begin
  33.     Proc2({ args });
  34.   end);
  35. end.

Чебурашка

  • Hero Member
  • *****
  • Posts: 568
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Re: Code shaping advice
« Reply #7 on: December 01, 2022, 10:05:54 pm »
Starting with FPC 3.3.1 you can do the following:

This is the right answer.
A syntax that transparently allows passing the parameters to the procedure that will be later injected in the middle of the wrapping method.
« Last Edit: December 01, 2022, 10:08:31 pm by tt »
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

Чебурашка

  • Hero Member
  • *****
  • Posts: 568
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Re: Code shaping advice
« Reply #8 on: December 02, 2022, 01:43:20 pm »
FPC 3.3.1

I forgot the most important question then: when is this going to be released as stable?
I truly hope asap because this feature is super useful/cool.
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

Thaddy

  • Hero Member
  • *****
  • Posts: 14201
  • Probably until I exterminate Putin.
Re: Code shaping advice
« Reply #9 on: December 02, 2022, 01:47:51 pm »
When its ready....
Specialize a type, not a var.

Чебурашка

  • Hero Member
  • *****
  • Posts: 568
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Re: Code shaping advice
« Reply #10 on: December 02, 2022, 01:55:30 pm »
When its ready....

Good old debian style :)
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Code shaping advice
« Reply #11 on: December 02, 2022, 05:19:31 pm »
I forgot the most important question then: when is this going to be released as stable?

Definitely not this year anymore. Maybe middle to late next year, at the latest I'd say 2024. But as always: this is without any guarantees.

Чебурашка

  • Hero Member
  • *****
  • Posts: 568
  • СЛАВА УКРАЇНІ! / Slava Ukraïni!
Re: Code shaping advice
« Reply #12 on: December 03, 2022, 09:47:04 am »
Definitely not this year anymore. Maybe middle to late next year, at the latest I'd say 2024. But as always: this is without any guarantees.

Glory to those who make the fpc. Now, middle of 2023 and after.
FPC 3.2.0/Lazarus 2.0.10+dfsg-4+b2 on Debian 11.5
FPC 3.2.2/Lazarus 2.2.0 on Windows 10 Pro 21H2

 

TinyPortal © 2005-2018