Recent

Author Topic: Encapsulating existing functions within a class without generating proxy code?  (Read 930 times)

jamie

  • Hero Member
  • *****
  • Posts: 5854
I am looking to make a number of related functions to live in a class or Record that already exists in the rtl, but I would like the name space be within a class, and I don't want the compiler to generate extra call code for this.

Below is a simple Class function that uses an Inline to call the sysutils.Now without it generating extra code, I simply want the compiler to call the original function when it gets accessed from the class.

 Is there a known way to do this directly in the Class/Record definition, like an "Alias" of some sort?
 
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$modeswitch AdvancedRecords}
  5.  
  6. interface
  7.  
  8. uses
  9.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  10.  
  11. type
  12. TTimeClass = Class
  13.  Class Function Now:TDateTime;
  14. end;
  15.  
  16.   { TForm1 }
  17.  
  18.   TForm1 = class(TForm)
  19.     Button1: TButton;
  20.     procedure Button1Click(Sender: TObject);
  21.   private
  22.  
  23.   public
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30.  
  31. {$R *.lfm}
  32. Class Function TTimeClass.Now:TDateTime; Inline;
  33. begin
  34.  Result := SysUtils.Now;
  35. end;
  36.  
  37. { TForm1 }
  38.  
  39. procedure TForm1.Button1Click(Sender: TObject);
  40. begin
  41.  Caption := DateTimeTostr(TTimeClass.Now);
  42. end;
  43.  
  44. end.
  45.  
  46.  

P.S.
 I am trying to get projects to work between Delphi and Lazarus.
The only true wisdom is knowing you know nothing

Fibonacci

  • Full Member
  • ***
  • Posts: 222
  • #PDK
I dont think so, you will need to make all that little inline functions

TRon

  • Hero Member
  • *****
  • Posts: 1868
Is there a known way to do this directly in the Class/Record definition, like an "Alias" of some sort?

Well, you can do this:
 
Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   classes, sysutils;
  7.  
  8. var
  9.   // Custom version of sysutils.now, invoking original sysutils.now function
  10.   // Drawbacks: no overloads, no intrinsics
  11.   Now : function: TDateTime = @sysutils.now;
  12.   RightStr : function(const S: string; Count: Integer): string = @sysutils.RightStr;
  13.   Sleep : procedure(milliseconds: Cardinal) = @sysutils.sleep;
  14.  
  15. var
  16.   SomeNow: TDateTime;
  17.  
  18. begin
  19.   // For functions without parameters we need to explicitely
  20.   // mention to invoke the function to avoid copying the
  21.   // contents of the variable
  22.   // This is not required in mode Delphi but atm it eludes me
  23.   // what modeswitch (if any) changes that behaviour
  24.   SomeNow := Now();
  25.   writeln('now = ', DateTimeToStr(Now()));
  26.  
  27.   // Function with parameters
  28.   writeln(RightStr('1234567890',1));
  29.  
  30.   sleep(10*1000);
  31.   writeln('done');
  32. end.
  33.  

I assume you are able to figure out how to drop that into a class/record ?

Fibonacci

  • Full Member
  • ***
  • Posts: 222
  • #PDK
// This is not required in mode Delphi but atm it eludes me
// what modeswitch (if any) changes that behaviour

Code: Pascal  [Select][+][-]
  1. {$modeswitch CLASSICPROCVARS}  // tp style procvars (no @ needed)
  2. {$modeswitch POINTERTOPROCVAR} // allows the assignement of pointers to procedure variables
  3.  
  4. uses SysUtils;
  5.  
  6. var
  7.   Now: function: TDateTime = @SysUtils.Now;
  8.  
  9. begin
  10.   writeln('Now = ', DateTimeToStr(Now));
  11. end.

Code: Pascal  [Select][+][-]
  1. {$modeswitch CLASSICPROCVARS}  // tp style procvars (no @ needed)
  2.  
  3. uses SysUtils;
  4.  
  5. var
  6.   Now: function: TDateTime = SysUtils.Now;
  7.  
  8. begin
  9.   writeln('Now = ', DateTimeToStr(Now));
  10. end.

BTW, I dont like this in Delphi, assigning procedures/functions like that. It looks like the returned value should be assigned, not the function. With @ its obvious.
« Last Edit: October 01, 2023, 05:04:49 am by Fibonacci »

TRon

  • Hero Member
  • *****
  • Posts: 1868
Splendid !

Thank you very much for that addition Fibonacci.

btw: for 3.2.2 it seems that modeswitch autoderef is also required.

Quote
BTW, I dont like this in Delphi, assigning procedures/functions like that. It looks like the returned value should be assigned, not the function. With @ its obvious.
I fully agree as for me it reads ambiguous. The way FPC does it makes it clear and indeed obvious.
« Last Edit: October 01, 2023, 05:35:37 am by TRon »

jamie

  • Hero Member
  • *****
  • Posts: 5854
let's not get carried away with this now :D

I just thought it to be nice to group a set of preexisting functions within a class/Record to make the code more manageable but not generate extra bloat over this.

 something like the TPath in Delphi is an example but I don't know if those members are directly linked to RTL functions or are proxy to some of them? But the concept is there to keep things cleaner.

 Also, one could make a record with some inner variables to localize some of the operations in specific code frags.
The only true wisdom is knowing you know nothing

TRon

  • Hero Member
  • *****
  • Posts: 1868
I just thought it to be nice to group a set of preexisting functions within a class/Record to make the code more manageable but not generate extra bloat over this.
You and me seem to have a different interpretation of what bloat means  :)

Quote
something like the TPath in Delphi is an example but I don't know if those members are directly linked to RTL functions or are proxy to some of them? But the concept is there to keep things cleaner.
Then have a look at how TPath is implemented ?

jamie

  • Hero Member
  • *****
  • Posts: 5854
I never had any intention of duplicating the Tpath class, that was just an example which is very poorly done since most of those functions already exist in the RTL anyways, so the idea was to simply get the compiler to make a direct call instead of the proxy.

 I don't think Delphi is smart enough to make a direct call like that.

also on the other hand, any class functions that do not exist in the RTL can be tinkered with.

I have not tried using INLINE with Delphi yet to see if that actually works to shrink things down but a few tests with fpc shows that it's capable of reducing proxy calls.

Have fun.


The only true wisdom is knowing you know nothing

TRon

  • Hero Member
  • *****
  • Posts: 1868
I don't think Delphi is smart enough to make a direct call like that.
No it does not, or at least didn't (I more or less stopped following the progress of Delphi after that unpronounceable named company took over).

But that is imho besides the point. Whether the compiler has some nice trick up it sleeves to do as you want does not matter as behind the scenes it would have to do exactly the same as shown. It is the normal way to setup a jump table (that what you refer to as proxy) that can be used to plug-and-pray. There can be found many of such plug&play 'tables' in the FPC tree.

The only other way I am aware of, but being even more ugly and for sure more bloated, is that you can lookup the addresses and the parameter list using debug info (can probably be done using (extended) rtti as well). Some scripting languages seem to be using the rtti route to locate the required information.

You could always try a feature request though I am not sure it would serve enough purpose for it to be justified (in the eyes of the judges).

 

TinyPortal © 2005-2018