View Issue Details

IDProjectCategoryView StatusLast Update
0034012FPCRTLpublic2018-07-31 11:34
ReporterThaddy de KoningAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityhave not tried
Status resolvedResolutionfixed 
PlatformallOSallOS Versionakk
Product Version3.1.1Product Build39471 
Target Version3.2.0Fixed in Version3.1.1 
Summary0034012: FEAURE REQUEST: generic IfThen
DescriptionI have this little snippet that is maybe a candidate for inclusion in either sysutils or math:
{$ifdef fpc}{$mode delphi}{$H+}{$endif}
   // generic ifthen..
    function IfThen<T>(val:boolean;const iftrue:T; const iffalse:T) :T; inline; overload;
    begin
      Result :=ifFalse;
      if val = true then Result := ifTrue
    end;
TagsNo tags attached.
Fixed in Revision39521
FPCOldBugId
FPCTarget
Attached Files

Activities

Thaddy de Koning

2018-07-20 19:46

reporter   ~0109592

FEATURE of course

Bart Broersma

2018-07-20 22:24

reporter   ~0109593

> if val = true
Why the superfluous "= true"?

Thaddy de Koning

2018-07-20 22:49

reporter   ~0109594

Last edited: 2018-07-20 22:51

View 2 revisions

Because the result needs to be initialized.
Could also write
if val = false then result := IsFalse<T> else result :=IfTrue<T>;

which is logically the same.

Bart Broersma

2018-07-21 01:16

reporter   ~0109595

Why not simply
if val then Result := ifTrue?

if Boolean condition then statement.
val already is a boolean condition, no need to compare to true/false.
Otherwise, why not:
if ((((val=true)=true)=true)=true)... ad infinitum.

Thaddy de Koning

2018-07-21 07:23

reporter   ~0109597

Last edited: 2018-07-21 07:25

View 3 revisions

Actually we both weren't paying attention by focussing on the shortcut.
In fact I do a double assignment operation if value is true, can be costly especially for managed types types.
We need a single assignment operation:
{$ifdef fpc}{$mode delphi}{$H+}{$endif}
   // generic ifthen..
    function IfThen<T>(Value:boolean;const IfTrue, IfFalse:T) :T; inline; overload;
    begin
       if Value then
         Result := IfTrue
       else
         Result := IfFalse;
    end;

This has only a single assignment operation. So if accepted it should like this.

Akira1364

2018-07-26 02:22

reporter   ~0109678

Last edited: 2018-07-26 05:30

View 4 revisions

This will clash with the non-generic IfThen in {$mode objfpc}, as you can't have generic methods with the same name as non-generic methods in it even with overloads (which seems like it might be a bug?)

In {$mode delphi} the overload modifier doesn't actually do anything for the generic method, also (you could have a non-generic IfThen directly before or after it with no overloads anywhere and it would still compile fine.)

Regardless, why not just name the method something completely different?

Thaddy de Koning

2018-07-26 08:20

reporter   ~0109679

Last edited: 2018-07-26 08:21

View 2 revisions

AFAIK there is no problem. I also introduced a generic RandomFrom<T> in math.
Similar code:
function RandomFrom(const AValues: array of Double): Double; overload;
function RandomFrom(const AValues: array of Integer): Integer; overload;
function RandomFrom(const AValues: array of Int64): Int64; overload;
{$if FPC_FULLVERSION >=30101}
generic function RandomFrom<T>(const AValues:array of T):T;
{$endif}

So there is no clash...What lead you to believe otherwise?

Akira1364

2018-07-26 15:42

reporter   ~0109689

Last edited: 2018-07-26 15:45

View 2 revisions

If you actually call one of the non-generic versions in {$mode objfpc}, you'll get:

"Error: Generics without specialization cannot be used as a type for a variable".

See this program for an example:

program Test;

{$mode objfpc}

function Add(const A, B: Integer): Integer; overload;
begin
  Result := A + B;
end;

generic function Add<T>(const A, B: T): T; overload;
begin
  Result := A + B;
end;

begin
  WriteLn(Add(1, 2));
  WriteLn(specialize Add<String>('Hello,', ' there!'));
end.

Again, if you ask me this seems like a bug as it probably should actually work, but it doesn't right now.

Thaddy de Koning

2018-07-27 05:59

reporter   ~0109699

Yes looks like a bug in objfpc mode. Can you open it as a separate issue?

Note mode delphi works as expected:
program Test2;
{$mode delphi}
function Add(const A, B: Integer): Integer; overload;
begin
  Result := A + B;
end;

function Add<T>(const A, B: T): T;overload;
begin
  Result := A + B;
end;

begin
  WriteLn(Add(1, 2));
  WriteLn(Add<String>('Hello,', ' there!'));
end.

Maciej Izak

2018-07-27 10:41

reporter   ~0109705

@Akira1364, @Thaddy AFAIK this is by design of objfpc mode...

Akira1364

2018-07-27 16:52

reporter   ~0109710

Last edited: 2018-07-27 18:49

View 5 revisions

Are you sure? It really doesn't seem intentional, for several reasons:

1) The error message raised clearly indicates that the compiler thinks you're trying to call the generic version, when you're actually calling the non-generic one.

2) That would mean that the overload modifier cannot in fact be used on generic methods at all (as it already does nothing and is not needed for generic methods in {$mode delphi}). If that's the case, why does the compiler even allow it on generics?

3) There's just not really any logical reason why it would work perfectly in {$mode delphi} and not {$mode objfpc}. That would be a clear, pointless disadvantage for {$mode objfpc}, that most people would not expect to be there (as shown in the comments here.) I can't imagine anyone attempting to use {$mode objfpc}, encountering this issue, and just going "oh, method overloading doesn't work, makes sense!"

Maciej Izak

2018-07-29 00:25

reporter   ~0109724

@Akira1364 Yes, I wish to be wrong (but sadly I was discussing about this topic with core...), probably just error message is incorrect - the concept is as designed.

objfpc is full of disadvantages (specialize & generic keywords, lack of overloading for generics). So if construction like this is disallowed in objfpc :

  generic TA<T> = record end;
  generic TA<T1, T2> = record end;

or

  generic TA = record end;
  generic TA<T> = record end;

I see no reason why they should allowing this

  function Add(const A, B: Integer): Integer;
  function Add<T>(const A, B: T): T;

In my opinion this has no sense too but I am not part of core anymore and I have no influence on FPC anymore.

Akira1364

2018-07-29 03:26

reporter   ~0109725

Last edited: 2018-07-29 03:31

View 6 revisions

Here's the thing though: it actually DOES work in {$mode ObjFPC} at the unit level, just not the program level. See here:

unit Unit1;

{$mode objfpc}{$H+}

interface

function Add(const A, B: Integer): Integer; overload;
generic function Add<T>(const A, B: T): T; overload;

implementation

function Add(const A, B: Integer): Integer;
begin
  Result := A + B;
end;

generic function Add<T>(const A, B: T): T;
begin
  Result := A + B;
end;

end.

That code compiles and works fine. Thus, I'm still quite certain this is a bug, and if someone told you it wasn't, there's three possibilities:

1) They misunderstood what exactly it was you were talking about
2) They don't actually understand themselves how generics really work in FPC in all situations (probably because they don't use them enough)
3) They were making things up to deflect attention away from it simply being a bug that they don't know how to fix (I really hope it's not this one!)

As far as the example you showed with records, yeah, that's total nonsense. Function overloading has absolutely nothing to do with specialized types, generic or not. (I mean no offense by the way, as you probably weren't the one to come up with it.)

Michael Van Canneyt

2018-07-29 11:40

administrator   ~0109728

Added in sysutils. Tested 4 programs:

Delphi mode:

program testifthen;
{$mode delphi}
uses math, sysutils;

begin
  Writeln(IfThen(false,1.0,2.0));
  Writeln(IfThen<AnsiString>(false,'1.0','2.0'));
end.

ObjFPC mode

program testifthen;
{$mode objfpc}
uses math, sysutils;

begin
  Writeln(IfThen(false,1.0,2.0));
  Writeln(specialize IfThen<AnsiString>(false,'1.0','2.0'));
end.

And the same, but with the order of the units in the uses clause inverted.

All work.

The proper solution where only the needed branch is evaluated is still pending, but needs compiler support.

Thaddy de Koning

2018-07-31 11:09

reporter   ~0109778

Last edited: 2018-07-31 11:11

View 2 revisions

Michael, I would like to contribute a small example towards documentation:

----
{$mode delphi}
uses sysutils;
type
  Ttest = function:string;

function SomeTestOne:string;
begin
 writestr(result,'Test1');
end;

function SomeTestTwo:string;
begin
 writestr(result,'Test2')
end;

var
  a:integer = 100;
  b:integer = 200;
  c:single = 0.001;
  d:single = 1.000;
  e:TTest = SomeTestOne;
  f:TTest = SomeTestTwo;
begin
  writeln(ifthen<integer>(true,a,b));
  writeln(ifthen<integer>(false,a,b));
  writeln(ifthen<single>(true,c,d):2:3);
  writeln(ifthen<single>(false,c,d):2:3);
  writeln(ifthen<Ttest>(true,e,f)); // executes e
  writeln(ifthen<Ttest>(false,e,f)); // executes f
end.
----

Thx for introducing this snippet. It can be very powerful as this example illustrates.

Michael Van Canneyt

2018-07-31 11:34

administrator   ~0109779

Added example in documentation, rev. 1502. Thank you !

Issue History

Date Modified Username Field Change
2018-07-20 19:45 Thaddy de Koning New Issue
2018-07-20 19:46 Thaddy de Koning Note Added: 0109592
2018-07-20 22:24 Bart Broersma Note Added: 0109593
2018-07-20 22:49 Thaddy de Koning Note Added: 0109594
2018-07-20 22:51 Thaddy de Koning Note Edited: 0109594 View Revisions
2018-07-21 01:16 Bart Broersma Note Added: 0109595
2018-07-21 07:23 Thaddy de Koning Note Added: 0109597
2018-07-21 07:25 Thaddy de Koning Note Edited: 0109597 View Revisions
2018-07-21 07:25 Thaddy de Koning Note Edited: 0109597 View Revisions
2018-07-21 09:49 Michael Van Canneyt Assigned To => Michael Van Canneyt
2018-07-21 09:49 Michael Van Canneyt Status new => assigned
2018-07-26 02:22 Akira1364 Note Added: 0109678
2018-07-26 05:05 Akira1364 Note Edited: 0109678 View Revisions
2018-07-26 05:05 Akira1364 Note Edited: 0109678 View Revisions
2018-07-26 05:30 Akira1364 Note Edited: 0109678 View Revisions
2018-07-26 08:20 Thaddy de Koning Note Added: 0109679
2018-07-26 08:21 Thaddy de Koning Note Edited: 0109679 View Revisions
2018-07-26 15:42 Akira1364 Note Added: 0109689
2018-07-26 15:45 Akira1364 Note Edited: 0109689 View Revisions
2018-07-27 05:59 Thaddy de Koning Note Added: 0109699
2018-07-27 10:41 Maciej Izak Note Added: 0109705
2018-07-27 16:52 Akira1364 Note Added: 0109710
2018-07-27 16:54 Akira1364 Note Edited: 0109710 View Revisions
2018-07-27 17:04 Akira1364 Note Edited: 0109710 View Revisions
2018-07-27 17:06 Akira1364 Note Edited: 0109710 View Revisions
2018-07-27 18:49 Akira1364 Note Edited: 0109710 View Revisions
2018-07-29 00:25 Maciej Izak Note Added: 0109724
2018-07-29 03:26 Akira1364 Note Added: 0109725
2018-07-29 03:27 Akira1364 Note Edited: 0109725 View Revisions
2018-07-29 03:28 Akira1364 Note Edited: 0109725 View Revisions
2018-07-29 03:29 Akira1364 Note Edited: 0109725 View Revisions
2018-07-29 03:29 Akira1364 Note Edited: 0109725 View Revisions
2018-07-29 03:31 Akira1364 Note Edited: 0109725 View Revisions
2018-07-29 11:40 Michael Van Canneyt Fixed in Revision => 39521
2018-07-29 11:40 Michael Van Canneyt Note Added: 0109728
2018-07-29 11:40 Michael Van Canneyt Status assigned => resolved
2018-07-29 11:40 Michael Van Canneyt Fixed in Version => 3.1.1
2018-07-29 11:40 Michael Van Canneyt Resolution open => fixed
2018-07-29 11:40 Michael Van Canneyt Target Version => 3.2.0
2018-07-31 11:09 Thaddy de Koning Note Added: 0109778
2018-07-31 11:09 Thaddy de Koning Status resolved => feedback
2018-07-31 11:09 Thaddy de Koning Resolution fixed => reopened
2018-07-31 11:11 Thaddy de Koning Note Edited: 0109778 View Revisions
2018-07-31 11:34 Michael Van Canneyt Note Added: 0109779
2018-07-31 11:34 Michael Van Canneyt Status feedback => resolved
2018-07-31 11:34 Michael Van Canneyt Resolution reopened => fixed