Recent

Author Topic: Generics and overloading  (Read 6120 times)

heju

  • New member
  • *
  • Posts: 7
Generics and overloading
« on: October 24, 2018, 07:23:27 pm »
Hi all,

playing around with generics for a while, however I stumbled over a problem which it seems I cannot figure out by my self:

I try to call a overloaded function from within a function specialized from a template.
The code below is a very simple example, but the problem seems quite reasonable, e.g. all calculations are the same for the special datatypes except some require rounding the result or something like that.
The idea was to move the data type dependent code to overloaded functions and keep all the rest in the template. (Bad idea ?  ::) )

Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. type
  4.   generic Tdostd<_T> = class
  5.     public
  6.       type Tvariable = _T;
  7.       function doit(a: Tvariable):Tvariable;
  8.   end;
  9.  
  10.  
  11. function myfun(a:double):double;
  12. begin
  13.   result:=a+1.0;
  14. end;
  15.  
  16.  
  17. function myfun(a:string):string;
  18. begin
  19.   result:=a+'Hi all';
  20. end;
  21.  
  22.  
  23. function Tdostd.doit(a: Tdostd.Tvariable):Tdostd.Tvariable;
  24. begin
  25.   result:=myfun(a);
  26. end;
  27.  
  28.  
  29. type
  30.   TdoitDouble = specialize Tdostd<double>;
  31.   TdoitString = specialize Tdostd<string>;
  32.  
  33.  
  34. begin
  35. end.
  36.  
  37.  

However the result is:

Compile Project, Target: project1: Exit code 256, Errors: 1, Hints: 2
project1.lpr(25,11) Error: Can't determine which overloaded function to call
project1.lpr(17,10) Hint: Found declaration: myfun(AnsiString):AnsiString;
project1.lpr(11,10) Hint: Found declaration: myfun(Double):Double;

Could you please give me a hint how to correct the code or the whole approach?

Thank you very much,

 ;)

jamie

  • Hero Member
  • *****
  • Posts: 6130
Re: Generics and overloading
« Reply #1 on: October 24, 2018, 09:27:59 pm »
Try appending "overload" on the functions
The only true wisdom is knowing you know nothing

heju

  • New member
  • *
  • Posts: 7
Re: Generics and overloading
« Reply #2 on: October 24, 2018, 10:33:24 pm »
Hmm, seems adding "overload" does not change anything. Still the same error msg.

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: Generics and overloading
« Reply #3 on: October 25, 2018, 12:12:30 am »
Could you please give me a hint how to correct the code or the whole approach?
The approach is valid, but the implementation requires adjustment. Here it works:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$LONGSTRINGS ON}
  3. {$APPTYPE CONSOLE}
  4.  
  5. type
  6.   generic TDoStd<_T> = class
  7.   public
  8.     class function DoIt(a: _T): _T; static;
  9.   end;
  10.  
  11. function MyFun(const a: Double): Double;
  12. begin
  13.   Result := a + 1.0;
  14. end;
  15.  
  16. function MyFun(const a: string): string;
  17. begin
  18.   Result := a + 'Hi all';
  19. end;
  20.  
  21. class function TDoStd.DoIt(a: _T): _T; static;
  22. begin
  23.   if TypeInfo(_T) = TypeInfo(Double) then
  24.     Double(Result) := MyFun(Double(a))
  25.   else
  26.     if TypeInfo(_T) = TypeInfo(string) then
  27.       string(Result) := MyFun(string(a))
  28.     else
  29.       Result := Default(_T);
  30. end;
  31.  
  32. type
  33.   TDoItDouble = specialize TDoStd<double>;
  34.   TDoItString = specialize TDoStd<string>;
  35.  
  36. begin
  37.   Writeln(TDoItDouble.DoIt(0.2):0:2);
  38.   Writeln(TDoItString.DoIt('Well, '));
  39.   Readln;
  40. end.
I used static to reduce the example, i.e. avoid creating and destroying the object.

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: Generics and overloading
« Reply #4 on: October 25, 2018, 12:33:25 am »
Of course, it is not very effective, because for each type generates its own function, with a sequential check if then else, where only one comparison will work.
In Delphi exists undocumented intrinsic routines, which allow generated more compact code:
Code: Pascal  [Select][+][-]
  1. class function TDoStd<_T>.DoIt(a: _T): _T;
  2. begin
  3.   case GetTypeKind(_T) of
  4.     tkFloat:
  5.       PDouble(@Result)^ := MyFun(PDouble(@a)^);
  6.     tkUString:
  7.       PString(@Result)^ := MyFun(PString(@a)^);
  8.   else
  9.     Result := Default(_T);
  10.   end;
  11. end;

But it is even better do not use generics:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$LONGSTRINGS ON}
  3. {$APPTYPE CONSOLE}
  4.  
  5. type
  6.   TDoStd = class
  7.   public
  8.     class function DoIt(const a: Double): Double; static;
  9.     class function DoIt(const a: string): string; static;
  10.   end;
  11.  
  12. function MyFun(const a: Double): Double;
  13. begin
  14.   Result := a + 1.0;
  15. end;
  16.  
  17. function MyFun(const a: string): string;
  18. begin
  19.   Result := a + 'Hi all';
  20. end;
  21.  
  22. class function TDoStd.DoIt(const a: Double): Double;
  23. begin
  24.   Result := MyFun(a);
  25. end;
  26.  
  27. class function TDoStd.DoIt(const a: string): string;
  28. begin
  29.   Result := MyFun(a);
  30. end;
  31.  
  32. begin
  33.   Writeln(TDoStd.DoIt(0.2):0:2);
  34.   Writeln(TDoStd.DoIt('Well, '));
  35.   Readln;
  36. end.

heju

  • New member
  • *
  • Posts: 7
Re: Generics and overloading
« Reply #5 on: October 27, 2018, 10:29:22 am »
Hi,

thanks for your replies, I didn't know the TypeInfo function.

I have one more question:
To me it seems with the TypeInfo solution, the type gets evaluated at runtime and the needed function is as well determined at runtime.
If this is true, its not what I understood the goal of generics is. For generics the compiler instantiates the needed functions at compile time, and therefore creating code as fast as code implemented manually for each data type.

Found this explanation
https://www.freepascal.org/docs-html/ref/refse57.html#x116-1380008.9
about operator overloading in conjunction with generics, here the compiler can correctly choose the needed "add" operator from the overloaded ones, something similar should be possible with overloaded functions, or not?
They state a limitation which might explain why my code is not working, however I do not know how to apply the given working solution for op overloading to function overloading  %)

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: Generics and overloading
« Reply #6 on: October 27, 2018, 11:50:01 am »
@ASerge
If you mean GetTypeKind? That is in FPC system.pas in 3.2.0 + and is documented. Or do you mean other intrinsics? Since you refer to Delphi?
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

ASerge

  • Hero Member
  • *****
  • Posts: 2242
Re: Generics and overloading
« Reply #7 on: October 27, 2018, 10:11:55 pm »
If you mean GetTypeKind? That is in FPC system.pas in 3.2.0 + and is documented. Or do you mean other intrinsics? Since you refer to Delphi?
You are right, in the trunk version it works:
Code: Pascal  [Select][+][-]
  1. {$MODE OBJFPC}
  2. {$LONGSTRINGS ON}
  3. {$APPTYPE CONSOLE}
  4.  
  5. type
  6.   generic TDoStd<_T> = class
  7.     class function DoIt(a: _T): _T; static;
  8.   end;
  9.  
  10. function MyFun(const a: Double): Double;
  11. begin
  12.   Result := a + 1.0;
  13. end;
  14.  
  15. function MyFun(const a: string): string;
  16. begin
  17.   Result := a + 'Hi all';
  18. end;
  19.  
  20. class function TDoStd.DoIt(a: _T): _T; static;
  21. begin
  22.   case GetTypeKind(_T) of
  23.     tkFloat:
  24.       Double(Result) := MyFun(Double(a));
  25.     tkAString:
  26.       string(Result) := MyFun(string(a));
  27.   else
  28.     Result := Default(_T);
  29.   end;
  30. end;
  31.  
  32. begin
  33.   Writeln(specialize TDoStd<double>.DoIt(0.2):0:2);
  34.   Writeln(specialize TDoStd<string>.DoIt('Well, '));
  35.   Readln;
  36. end.

Is there a link to the documentation with GetTypeKind?

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: Generics and overloading
« Reply #8 on: October 28, 2018, 09:03:35 am »
You have to generate the documentation for trunk yourself.
Ah, sorry: a grep showed me it is not yet documented in the real docs.
Anyway, it is in system. I asked to document it on the bugtracker.
[edit] It is now documented - tnx, Michael! - and target is 3.2.0 so it makes the release.
« Last Edit: October 28, 2018, 02:38:41 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: Generics and overloading
« Reply #9 on: October 28, 2018, 03:41:00 pm »
Just run the program in the IDE and step over and done with the code.
You can inspect variables (what's their value) then.

Otherwise, after each assignement ( := ), and as a first step in each loop do a writelln('avariable = ',avariable); where avariable is the variable you are absorbed in.
This is not about debugging.....
« Last Edit: October 28, 2018, 03:45:55 pm by Martin_fr »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Generics and overloading
« Reply #10 on: October 28, 2018, 03:43:13 pm »
This is not about debugging.....
and that was clearly a spam advertising. Thaddy...... slow down a bit and look closer around you.
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

heju

  • New member
  • *
  • Posts: 7
Re: Generics and overloading
« Reply #11 on: October 28, 2018, 06:21:54 pm »
What is going on here, where do the comments come from?  %)

Anyway... I would be really interested in an explanation why the original code is not working, why cant the compiler select the correct overloaded function while e.g. instantiating the code for double type. In this step the data type is obviously known.

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: Generics and overloading
« Reply #12 on: October 28, 2018, 06:58:36 pm »
Really simple.
Both ASege and I gave valid answers.
ASerge referred to an -actually not - missing feature compared to Freepascal.
Some idiot added something completely out of context.
Problem solved, because ASege's example simply works in FPC 3.2 (not in 3.0.4, it is a new feature).
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

heju

  • New member
  • *
  • Posts: 7
Re: Generics and overloading
« Reply #13 on: October 28, 2018, 07:37:42 pm »
@Thaddy: No reason to get upset...
I appreciate the given solutions and the new feature I learnt.

However, as I wrote already twice: My question is not really about getting this small piece of code to run by any means, but to understand how the given solution works:

Does it evaluate the type at run time and then choose the appropriate function, cause if this is the case it seems very ineffective and I was asking if there is a way to make the compiler select the function at compile time. (as it is possible for operator overloading)

Thaddy

  • Hero Member
  • *****
  • Posts: 14373
  • Sensorship about opinions does not belong here.
Re: Generics and overloading
« Reply #14 on: October 28, 2018, 07:43:35 pm »
That's a bit more complex:
RTTI information can be used both at compile and runtime. E,g. if something can be resolved at compile time then the compiler optimizes that. (part of the reason some rtti has moved to system).
However, rtti can be used to solve tasks at runtime and in that case it can not be optimized in the same manner.
So actually both scenario's apply.
« Last Edit: October 28, 2018, 07:45:51 pm by Thaddy »
Object Pascal programmers should get rid of their "component fetish" especially with the non-visuals.

 

TinyPortal © 2005-2018