Lazarus

Free Pascal => General => Topic started by: avk on September 22, 2020, 01:18:20 pm

Title: [SOLVED]Generics and Include/Exclude
Post by: avk on September 22, 2020, 01:18:20 pm
Suppose we need a procedure that, depending on the condition, includes or excludes a given element from the set and we want the procedure to accept any type of set as a parameter. Using a generic procedure for this seems like a good option:
Code: Pascal  [Select][+][-]
  1. program test;
  2.  
  3. {$mode delphi}
  4.  
  5. type
  6.   TElem = (One, Two, Three, Four, Five);
  7.   TSet  = set of TElem;
  8.  
  9. procedure TurnSetElem<TSet, TElem>(var aSet: TSet; aElem: TElem; aOn: Boolean);
  10. begin
  11.   if aOn then
  12.     Include(aSet, aElem)
  13.   else
  14.     Exclude(aSet, aElem);
  15. end;
  16.  
  17. var
  18.   s: TSet = [];
  19.   e: TElem;
  20.  
  21. begin
  22.   for e in s do
  23.     WriteLn(e);
  24.   TurnSetElem<TSet, TElem>(s, Two, True);
  25.   TurnSetElem<TSet, TElem>(s, Five, True);
  26.   for e in s do
  27.     WriteLn(e);
  28. end.
  29.  
compile-time error(both 3.2.0 and 3.3.1):
Code: Text  [Select][+][-]
  1. test.lpr(12,5) Error: Type mismatch
  2. test.lpr(14,5) Error: Type mismatch
  3.  

But if the procedure is slightly changed:
Code: Pascal  [Select][+][-]
  1. procedure TurnSetElem<TSet, TElem>(var aSet: TSet; aElem: TElem; aOn: Boolean);
  2. begin
  3.   if aOn then
  4.     aSet += [aElem]
  5.   else
  6.     aSet -= [aElem] ;
  7. end;
  8.  
then it compiles and works as expected.

Why is there such discrimination?
Title: Re: Generics and Include/Exclude
Post by: ASerge on September 22, 2020, 08:07:36 pm
Code: Pascal  [Select][+][-]
  1. {$APPTYPE CONSOLE}
  2. {$MODE DELPHI}
  3.  
  4. procedure TurnSetElem<TSet, TElem>(var ASet: TSet; AElem: TElem; AOn: Boolean);
  5.  
  6.   procedure TurnSetUniveral(var ASet; const AItem; AOn: Boolean);
  7.   type
  8.     TItem = 0..31;
  9.     TItemSet = set of TItem;
  10.   begin
  11.     if AOn then
  12.       Include(TItemSet(ASet), TItem(AItem))
  13.     else
  14.       Exclude(TItemSet(ASet), TItem(AItem))
  15.   end;
  16.  
  17. begin
  18.   TurnSetUniveral(ASet, AElem, AOn)
  19. end;
  20.  
  21. procedure Test;
  22. type
  23.   TElem = (One, Two, Three, Four, Five);
  24.   TSet  = set of TElem;
  25. var
  26.   LSet: TSet = [];
  27.   Elem: TElem;
  28. begin
  29.   for Elem in LSet do
  30.     WriteLn(Elem);
  31.   TurnSetElem<TSet, TElem>(LSet, Two, True);
  32.   TurnSetElem<TSet, TElem>(LSet, Five, True);
  33.   for Elem in LSet do
  34.     WriteLn(Elem);
  35. end;
  36.  
  37. begin
  38.   Test;
  39.   Readln;
  40. end.
Title: Re: Generics and Include/Exclude
Post by: avk on September 23, 2020, 06:26:15 am
@ASerge, yeah, I've already seen code like this in this post (https://forum.lazarus.freepascal.org/index.php/topic,51516.msg378327.html#msg378327). But the question is that the fact that the compiler does not accept Include/Exclude looks like a bug.
Title: Re: Generics and Include/Exclude
Post by: ASerge on September 23, 2020, 04:45:05 pm
But the question is that the fact that the compiler does not accept Include/Exclude looks like a bug.
I thought you needed a general solution. And the question is not difficult to answer. Built-in functions Include and Exclude expect only sets. The addition operation can be overloaded, i.e. it is valid.
Title: Re: Generics and Include/Exclude
Post by: avk on September 23, 2020, 07:23:14 pm
Built-in functions Include and Exclude expect only sets.

And what does it get if not a set?
Some time ago, such a stupid function(containing the built-in function Ord(), which accepts only ordinals)
Code: Pascal  [Select][+][-]
  1. function GenSumOrd<T>(a, b: T): T;
  2. begin
  3.   Result := Ord(a) + Ord(b);
  4. end;
  5.  
flatly refused to compile with any version of the compiler. It now compiles(if specialized with ordinal type), at least using a trunk compiler(it looks like the changes weren't merged in 3.2.1). It seems with Include/Exclude the same situation.
Title: Re: Generics and Include/Exclude
Post by: PascalDragon on September 23, 2020, 10:02:04 pm
Why is there such discrimination?

The thing is that intrinsics are made aware of generics on a case by case base. This way we can ensure that they handle generics as type safe as reasonably possible. In this case no one had needed them up to now, so no one implemented them.

Thus please report this as a bug.
Title: Re: Generics and Include/Exclude
Post by: avk on September 24, 2020, 05:50:36 am
Thank you, done (https://bugs.freepascal.org/view.php?id=37806).

BTW, what about this case?
Code: Pascal  [Select][+][-]
  1. type
  2.   TSetUtil<TElem> = record
  3.   type
  4.     TSet = set of TElem;
  5.     class procedure TurnElem(var aSet: TSet; aElem: TElem; aOn: Boolean); static;
  6.   end;
  7.  
Title: Re: Generics and Include/Exclude
Post by: Thaddy on September 24, 2020, 08:29:29 am
Note you may be able to use the new generic ifthen<> from sysutils in such a way? 3.2.0 +
Title: Re: Generics and Include/Exclude
Post by: PascalDragon on September 24, 2020, 09:16:45 am
Thank you, done (https://bugs.freepascal.org/view.php?id=37806).

Thank you.

BTW, what about this case?

I had thought a bit about that as well. Maybe I should adjust some restrictions a bit considering that we have a more template-like approach anyway.
Title: Re: Generics and Include/Exclude
Post by: avk on September 24, 2020, 10:14:08 am
Note you may be able to use the new generic ifthen<> from sysutils in such a way?

I cannot figure out how I could use IfThen in this case. Maybe you can suggest some way?

I had thought a bit about that as well. Maybe I should adjust some restrictions a bit considering that we have a more template-like approach anyway.

Is something required of me?
Title: Re: Generics and Include/Exclude
Post by: Thaddy on September 24, 2020, 11:42:16 am
I cannot figure out how I could use IfThen in this case. Maybe you can suggest some way?
Well, the idea is that the generic ifthen can take functional and procedural parameters to allow different execution paths.

That is how I designed it.

Afaik Michael has also added an example to the documentation for that based on my own example when I submitted that feature.
It may take a little redesign of your code, but to me it looks possible.
The signature is like this:
Code: Pascal  [Select][+][-]
  1. generic function IfThen<T>(val:boolean;const iftrue:T; const iffalse:T) :T; inline; overload;
Where T can be anything, including procedural/functional types.

As usual, what Sven suggested gives even more options if implemented.
Title: Re: Generics and Include/Exclude
Post by: PascalDragon on September 24, 2020, 01:58:25 pm
I had thought a bit about that as well. Maybe I should adjust some restrictions a bit considering that we have a more template-like approach anyway.

Is something required of me?

No, not really. :)
Title: Re: Generics and Include/Exclude
Post by: avk on September 25, 2020, 05:32:37 pm
@PascalDragon, thanks a lot. The procedure now compiles and works as expected. But will these changes be merged in 3.2.1?
Title: Re: [SOLVED]Generics and Include/Exclude
Post by: PascalDragon on September 26, 2020, 10:00:27 am
I'll ask for them to be merged, yes, they shouldn't be that invasive.
TinyPortal © 2005-2018