Recent

Author Topic: Overload inconsistency with Delphi  (Read 5694 times)

BjPascal

  • New Member
  • *
  • Posts: 15
Overload inconsistency with Delphi
« on: July 16, 2018, 08:52:40 am »
using Lazarus 1.9.0 in Delphi-mode and FPC 3.1.1 on Win7 x64

I am using these overloads:
Code: Pascal  [Select][+][-]
  1.  function  When(Wahr:Boolean; TrueValue, FalseValue: String): String; overload;
  2.  function  When(Wahr:Boolean; TrueValue, FalseValue: RawByteString): RawByteString; overload;
  3.  function  When(Wahr:Boolean; TrueValue, FalseValue: integer): integer; overload;
  4.  

example calls
Code: Pascal  [Select][+][-]
  1. var
  2.  s: string;
  3.  i: integer;
  4. begin
  5.  i:= when(true, 1, 2);
  6.  s:= when(true, 'yes', 'no');
  7.  

line #6 fails with "Can't determine which overloaded function to call"

It compiles only when removing the RawByteString overload.
But Delphi Seattle compiles properly, and stepping thru shows it indeed calls the string variant.

Then I checked this:

Code: Pascal  [Select][+][-]
  1. var
  2.  rb,rb1,rb2: RawByteString;
  3. begin
  4.  rb:= when(true, 'yes', 'no');
  5.  rb:= when(true, rb1,rb2);
  6.  

Line #4 calls the string variant, line #5 the RawByteString variant.

So for string constants the compiler should should pick the string variant, and not complain.


mangakissa

  • Hero Member
  • *****
  • Posts: 1131
Re: Overload inconsistency with Delphi
« Reply #1 on: July 16, 2018, 09:05:39 am »
Whats happens if you're using variant in place of RawByteString?
Lazarus 2.06 (64b) / FPC 3.0.4 / Windows 10
stucked on Delphi 10.3.1

hnb

  • Sr. Member
  • ****
  • Posts: 270
Re: Overload inconsistency with Delphi
« Reply #2 on: July 16, 2018, 09:34:46 am »
you need to compile your code with DELPHIUNICODE mode. In Delphi you can't to compile the following code:

Code: Pascal  [Select][+][-]
  1. function  When(Wahr:Boolean; TrueValue, FalseValue: AnsiString): AnsiString; overload;
  2. function  When(Wahr:Boolean; TrueValue, FalseValue: RawByteString): RawByteString; overload;
  3. function  When(Wahr:Boolean; TrueValue, FalseValue: integer): integer; overload;

above code is equivalent of your code in DELPHI mode in FPC. Delphi mode means that all strings are declared as AnsiString, in DelphiUnicode each string is UnicodeString.
« Last Edit: July 16, 2018, 09:37:27 am by hnb »
Checkout NewPascal initiative and donate beer - ready to use tuned FPC compiler + Lazarus for mORMot project

best regards,
Maciej Izak

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Overload inconsistency with Delphi
« Reply #3 on: July 16, 2018, 09:22:14 pm »
It compiles only when removing the RawByteString overload.
But Delphi Seattle compiles properly, and stepping thru shows it indeed calls the string variant.

In Delphi 2009 and newer String is an alias for UnicodeString. However unless $modeswitch unicodestring is given the default in FPC (with $H+ set (the default in Delphi modes)) is AnsiString. So you should explicitely declare your String overload as UnicodeString (this will work in Delphi as well).

Please note that hnb's suggestion of using DelphiUnicode mode is not really useable as of right now as the RTL and LCL would still assume that String is AnsiString thus leading to problems like incorrect virtual overrides and such.

BjPascal

  • New Member
  • *
  • Posts: 15
Re: Overload inconsistency with Delphi
« Reply #4 on: July 20, 2018, 02:38:39 pm »
Many thanks for all your explanations.

The penny really dropped with the suggestion to use variants.
The When is a look-alike of the inline 'if' that we know in C
( r= condition ? trueValue : falseValue )
and I always wondered whether there is a simpler approach than using overloads for each possible type. The answer is: Variants.

So I replaced all my When's by
Code: Pascal  [Select][+][-]
  1. function When(Wahr:Boolean; TrueValue, FalseValue: variant): variant; overload;
  2. begin
  3.   if wahr then
  4.    Result := TrueValue
  5.   else
  6.    result := FalseValue;
  7. end;
  8.  

However this still caused compilation errors for Char and RawByteString.
So I added 2 overloads for these types.
And now it compiles/runs fine on Lazarus, Delphi-5 and Delphi-Seattle.

When doing this test:
Code: Pascal  [Select][+][-]
  1.  s:= when(true, 'yes', 'no');
  2.  
Lazarus calls the RawByteString version.
Delphi-Seattle calls the Variant version
Delphi-5 calls the Variant version (it does not have RawByteString)


Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Overload inconsistency with Delphi
« Reply #5 on: July 20, 2018, 03:38:46 pm »
FPC has several ifthen functions in math and strutils.
No generic one yet, but
Code: Pascal  [Select][+][-]
  1. {$ifdef fpc}{$mode delphi}{$H+}{$endif}
  2.    // generic ifthen..
  3.     function IfThen<T>(val:boolean;const iftrue:T; const iffalse:T) :T; inline; overload;
  4.     begin
  5.       Result :=ifFalse;
  6.       if val = true then Result := ifTrue
  7.     end;
  8. begin  
  9.   writeln(Ifthen<integer>(false,100,1));
  10.   writeln(Ifthen<integer>(true,100,1));
  11.   writeln(ifthen<unicodestring>(false,'Foo','bar'));
  12.   writeln(ifthen<unicodestring>(true,'Foo','bar'));
  13. end.
You may find the above version far more convenient.
« Last Edit: July 20, 2018, 03:50:19 pm by Thaddy »
Specialize a type, not a var.

BjPascal

  • New Member
  • *
  • Posts: 15
Re: Overload inconsistency with Delphi
« Reply #6 on: July 20, 2018, 04:04:22 pm »
not really.
These require to much typing and are hard to read, especially in longer expressions.
"when" without any <xx> is way simpler, and clearer.
Also it needs to work in Delphi

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Overload inconsistency with Delphi
« Reply #7 on: July 20, 2018, 05:11:13 pm »
Yes, but your when function is duplicating code that has already been in the RTL for years: the IfThen functions from units math and strutils.... They do the same as your When...
What my function does is that it is guaranteed that it is called for the proper variable type (even objects, arrays or records).
« Last Edit: July 20, 2018, 05:25:16 pm by Thaddy »
Specialize a type, not a var.

Akira1364

  • Hero Member
  • *****
  • Posts: 561
Re: Overload inconsistency with Delphi
« Reply #8 on: July 20, 2018, 11:07:29 pm »
not really.
These require to much typing and are hard to read, especially in longer expressions.
"when" without any <xx> is way simpler, and clearer.
Also it needs to work in Delphi

Thaddy's code does work in Delphi.

So would this version of your original method, as well as in FPC with {$mode Delphi}:

Code: Pascal  [Select][+][-]
  1. function When<T>(const Value: Boolean; const Good, Bad: T): T; inline;
  2. begin
  3.   case Value of
  4.     True: Result := Good;
  5.     False: Result := Bad;
  6.   end;
  7. end;

The one implementation is all you need. No overloads necessary! Then you can even do stuff like this:

Code: Pascal  [Select][+][-]
  1. program Test;
  2.  
  3. {$mode Delphi} //get rid of this line when actually using Delphi
  4.  
  5. uses Classes;
  6.  
  7.   function When<T>(const Value: Boolean; const Good, Bad: T): T; inline;
  8.   begin
  9.     case Value of
  10.       True: Result := Good;
  11.       False: Result := Bad;
  12.     end;
  13.   end;
  14.  
  15. var
  16.   A: TClass = TPersistent;
  17.   B: TClass = TObject;
  18.  
  19. begin
  20.   WriteLn(When<TClass>(A.ClassParent = B, TPersistent, TObject).ClassName);
  21. end.
« Last Edit: July 20, 2018, 11:43:18 pm by Akira1364 »

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Overload inconsistency with Delphi
« Reply #9 on: July 20, 2018, 11:27:12 pm »
Actually, I am not quite sure it would work in Delphi, because function overloading is a FreePascal first. I have to check that.
The Delphi modes are for accepting Delphi syntax. It doesn't mean that FPC code written in Delphi mode would work in Delphi. There are (quite a few, i.e. a lot of) differences in what FPC can and Delphi can't and also a few (less, but important) things that FPC can't (a.t.m.) compared to newer newer Delphi versions: e.g. attributes, anonymous methods and Thread monitor/Parallel for.
Be aware of that.
Specialize a type, not a var.

Akira1364

  • Hero Member
  • *****
  • Posts: 561
Re: Overload inconsistency with Delphi
« Reply #10 on: July 20, 2018, 11:40:37 pm »
Well, my point was that there's no reason for it to have the "overload" modifier at all if it's a generic function. That's the whole purpose of generics.
« Last Edit: July 20, 2018, 11:49:11 pm by Akira1364 »

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Overload inconsistency with Delphi
« Reply #11 on: July 21, 2018, 06:48:54 pm »
Delphi does not support global generic routines, only as method of a class or record. It's an FPC extension I thought is useful even in mode Delphi (with Delphi's generic syntax). *shrugs*

Akira1364

  • Hero Member
  • *****
  • Posts: 561
Re: Overload inconsistency with Delphi
« Reply #12 on: July 21, 2018, 07:53:50 pm »
Huh, I could have sworn it did! Just checked though and you're right (although interestingly, the Delphi IDE has no problem parsing them, and displays them properly in the mouse-over popup menu.) I guess that's a clear "plus one" for FPC then.

Things like the following do compile in Delphi, though (which makes it even more odd that it doesn't allow normal generic methods):

Code: Pascal  [Select][+][-]
  1. type TGenericFunctionType<T> = function(const A: T): T;

Also, as far as what "BjPascal" was trying to accomplish, changing my example to something like this for the Delphi version works fine:

Code: Pascal  [Select][+][-]
  1. program Test;
  2.  
  3. uses System.Classes;
  4.  
  5. type
  6.   TEvaluator = record
  7.     class function When<T>(const Value: Boolean; const Good, Bad: T): T; inline; static;
  8.   end;
  9.  
  10. class function TEvaluator.When<T>(const Value: Boolean; const Good, Bad: T): T;
  11. begin
  12.   case Value of
  13.     True: Result := Good;
  14.     False: Result := Bad;
  15.   end;
  16. end;
  17.  
  18. var
  19.   A: TClass = TPersistent;
  20.   B: TClass = TObject;
  21.  
  22. begin
  23.   WriteLn(TEvaluator.When<TClass>(A.ClassParent = B, TPersistent, TObject).ClassName);
  24. end.
« Last Edit: July 21, 2018, 08:25:00 pm by Akira1364 »

Thaddy

  • Hero Member
  • *****
  • Posts: 14197
  • Probably until I exterminate Putin.
Re: Overload inconsistency with Delphi
« Reply #13 on: July 21, 2018, 11:03:46 pm »
Yes. That's possible. But you missed the point that FPC can write stand-alone generic functions and procedures: without a class. There's a big difference. See my example. Does not work in Delphi, but works in FPC.
Specialize a type, not a var.

Akira1364

  • Hero Member
  • *****
  • Posts: 561
Re: Overload inconsistency with Delphi
« Reply #14 on: July 21, 2018, 11:57:11 pm »
Uh, no I didn't? I just had it in my head that it was also possible to have standalone generic functions in Delphi, so I thought your example and the example in my original comment would work for both it and FPC.
« Last Edit: July 22, 2018, 12:05:08 am by Akira1364 »

 

TinyPortal © 2005-2018